MechAssault Wiki mechassaultwikitide https://mechassault.wikitide.org/wiki/Main_Page MediaWiki 1.40.0 first-letter Media Special Talk User User talk MechAssault Wiki MechAssault Wiki talk File File talk MediaWiki MediaWiki talk Template Template talk Help Help talk Category Category talk Campaign Campaign talk TimedText TimedText talk Module Module talk CNBanner CNBanner talk Template:Infobox videogame 10 482 962 2012-08-13T09:42:39Z infoboxes>Anthony Appleyard 0 [[WP:AES|←]]Redirected page to [[Template:Infobox video game]] wikitext text/x-wiki #REDIRECT [[Template:Infobox video game]] b7846d88cb815822ebd1fd4960b74cb4513e5a6f Template:Vgrelease 10 459 918 2013-10-19T16:31:20Z wikipedia>WOSlinker 0 Changed protection level of Template:Vgrelease: allow template editors to modify ([Edit=Protected template] (indefinite) [Move=Protected template] (indefinite)) wikitext text/x-wiki #REDIRECT [[Template:Video game release]] cc02016a051e4f709be3edd3e9855ca3079d45d6 Module:!- 828 505 1016 2013-11-17T11:54:04Z string2>Mr. Stradivarius 0 Changed protection level of Template:!-: [[WP:High-risk templates|Highly visible template]]: reinstate full-protection - this should never be edited ([Edit=Block all non-admin users] (indefinite) [Move=Block all non-admin users] (indefinite)) wikitext text/x-wiki |-<noinclude> {{documentation}} </noinclude> 19a1b27b5273caa6fd83a1208fb704afa9ee7c04 Module:Namespace detect 828 408 816 2014-04-05T17:01:23Z wikipedia>Mr. Stradivarius 0 use demopage instead of page as the main "page" parameter Scribunto text/plain --[[ -------------------------------------------------------------------------------- -- -- -- NAMESPACE DETECT -- -- -- -- This module implements the {{namespace detect}} template in Lua, with a -- -- few improvements: all namespaces and all namespace aliases are supported, -- -- and namespace names are detected automatically for the local wiki. The -- -- module can also use the corresponding subject namespace value if it is -- -- used on a talk page. Parameter names can be configured for different wikis -- -- by altering the values in the "cfg" table in -- -- Module:Namespace detect/config. -- -- -- -------------------------------------------------------------------------------- --]] local data = mw.loadData('Module:Namespace detect/data') local argKeys = data.argKeys local cfg = data.cfg local mappings = data.mappings local yesno = require('Module:Yesno') local mArguments -- Lazily initialise Module:Arguments local mTableTools -- Lazily initilalise Module:TableTools local ustringLower = mw.ustring.lower local p = {} local function fetchValue(t1, t2) -- Fetches a value from the table t1 for the first key in array t2 where -- a non-nil value of t1 exists. for i, key in ipairs(t2) do local value = t1[key] if value ~= nil then return value end end return nil end local function equalsArrayValue(t, value) -- Returns true if value equals a value in the array t. Otherwise -- returns false. for i, arrayValue in ipairs(t) do if value == arrayValue then return true end end return false end function p.getPageObject(page) -- Get the page object, passing the function through pcall in case of -- errors, e.g. being over the expensive function count limit. if page then local success, pageObject = pcall(mw.title.new, page) if success then return pageObject else return nil end else return mw.title.getCurrentTitle() end end -- Provided for backward compatibility with other modules function p.getParamMappings() return mappings end local function getNamespace(args) -- This function gets the namespace name from the page object. local page = fetchValue(args, argKeys.demopage) if page == '' then page = nil end local demospace = fetchValue(args, argKeys.demospace) if demospace == '' then demospace = nil end local subjectns = fetchValue(args, argKeys.subjectns) local ret if demospace then -- Handle "demospace = main" properly. if equalsArrayValue(argKeys.main, ustringLower(demospace)) then ret = mw.site.namespaces[0].name else ret = demospace end else local pageObject = p.getPageObject(page) if pageObject then if pageObject.isTalkPage then -- Get the subject namespace if the option is set, -- otherwise use "talk". if yesno(subjectns) then ret = mw.site.namespaces[pageObject.namespace].subject.name else ret = 'talk' end else ret = pageObject.nsText end else return nil -- return nil if the page object doesn't exist. end end ret = ret:gsub('_', ' ') return ustringLower(ret) end function p._main(args) -- Check the parameters stored in the mappings table for any matches. local namespace = getNamespace(args) or 'other' -- "other" avoids nil table keys local params = mappings[namespace] or {} local ret = fetchValue(args, params) --[[ -- If there were no matches, return parameters for other namespaces. -- This happens if there was no text specified for the namespace that -- was detected or if the demospace parameter is not a valid -- namespace. Note that the parameter for the detected namespace must be -- completely absent for this to happen, not merely blank. --]] if ret == nil then ret = fetchValue(args, argKeys.other) end return ret end function p.main(frame) mArguments = require('Module:Arguments') local args = mArguments.getArgs(frame, {removeBlanks = false}) local ret = p._main(args) return ret or '' end function p.table(frame) --[[ -- Create a wikitable of all subject namespace parameters, for -- documentation purposes. The talk parameter is optional, in case it -- needs to be excluded in the documentation. --]] -- Load modules and initialise variables. mTableTools = require('Module:TableTools') local namespaces = mw.site.namespaces local cfg = data.cfg local useTalk = type(frame) == 'table' and type(frame.args) == 'table' and yesno(frame.args.talk) -- Whether to use the talk parameter. -- Get the header names. local function checkValue(value, default) if type(value) == 'string' then return value else return default end end local nsHeader = checkValue(cfg.wikitableNamespaceHeader, 'Namespace') local aliasesHeader = checkValue(cfg.wikitableAliasesHeader, 'Aliases') -- Put the namespaces in order. local mappingsOrdered = {} for nsname, params in pairs(mappings) do if useTalk or nsname ~= 'talk' then local nsid = namespaces[nsname].id -- Add 1, as the array must start with 1; nsid 0 would be lost otherwise. nsid = nsid + 1 mappingsOrdered[nsid] = params end end mappingsOrdered = mTableTools.compressSparseArray(mappingsOrdered) -- Build the table. local ret = '{| class="wikitable"' .. '\n|-' .. '\n! ' .. nsHeader .. '\n! ' .. aliasesHeader for i, params in ipairs(mappingsOrdered) do for j, param in ipairs(params) do if j == 1 then ret = ret .. '\n|-' .. '\n| <code>' .. param .. '</code>' .. '\n| ' elseif j == 2 then ret = ret .. '<code>' .. param .. '</code>' else ret = ret .. ', <code>' .. param .. '</code>' end end end ret = ret .. '\n|-' .. '\n|}' return ret end return p a4757000273064f151f0f22dc0e139092e5ff443 Template:Video game reviews 10 468 936 2014-05-22T08:46:55Z wikipedia>Paine Ellsworth 0 update wikitext text/x-wiki {{#invoke:Video game reviews|reviewbox }}<noinclude>{{template doc}}<!-- Add categories to the /doc sub-page and interwikis to Wikidata. --></noinclude> 4c0bdd60be06cf8fbc5260b0796615a76b14a075 Template:Pagetype 10 404 808 2014-07-09T08:29:38Z wikipedia>Callanecc 0 Changed protection level of Template:Pagetype: [[WP:High-risk templates|Highly visible template]]: With more than 5.5 million transclusions and cascade protections this should be full protected as well ([Edit=Allow only administrators] (indefinite) [Move= wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:pagetype|main}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> b8e6aa66678cd57877ea2c607372a45070f030a7 Module:!! 828 506 1018 2014-11-04T14:12:38Z string2>Pigsonthewing 0 stet wikitext text/x-wiki ||<noinclude> {{documentation}} </noinclude> cfbaaca3cb3edae590f75b87fb775d1be21774a4 Template:Icon 10 435 870 2015-01-23T10:29:55Z wikipedia>Mr. Stradivarius 0 switch this to use [[Module:Icon]] wikitext text/x-wiki {{#invoke:Icon|main}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> bd5b855953c5eec9d9c48400aa39315cb4218558 Module:Hlist 828 499 998 2015-07-04T15:13:14Z string2>Penwhale 0 Changed protection level of Template:Hlist: [[WP:High-risk templates|Highly visible template]] ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite)) wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:list|horizontal}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 9e3824c2e3c0e0dbef2f37556ac0b994987fecf9 Template:MechWarrior series 10 476 952 2015-07-09T02:57:48Z wikipedia>Czar 0 merged per TfD wikitext text/x-wiki #REDIRECT [[Template:BattleTech Universe]] {{R from merge}} b1f8bc05076449b55471afcded8ec7fda35faabf Template:Div col end 10 323 641 2015-10-03T15:58:07Z wikipedia>NeilN 0 Changed protection level of Template:Div col end: per request at [[WP:RFPP]] ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite)) wikitext text/x-wiki <includeonly></div></includeonly><noinclude> {{Documentation|Template:Div col/doc}} </noinclude> 78088d41c21d779e3722f220fcc9773dfbbc1e4f Module:(( 828 509 1024 2015-11-13T02:17:23Z string2>Fuhghettaboutit 0 Changed protection level of Template:((: when I changed this to add template editors, I didn't realize it had cascding protection—so all my change did was make it appear as if it could be edited by them ([Edit=Allow only administrators] (indefinite)... wikitext text/x-wiki {{<noinclude> {{documentation}} </noinclude> 3f1af626705a06bc9cdea766736e292d321af5a3 Template:PAGENAMEBASE 10 430 860 2016-03-06T09:37:56Z wikipedia>Cabayi 0 per edit request & discussion wikitext text/x-wiki {{{{{|safesubst:}}}#Invoke:String|replace|{{{1|{{{{{|safesubst:}}}PAGENAME}}}}}|%s+%b()$||1|false}}<noinclude> {{documentation}} </noinclude> f23a5d434cb5b0baac5e1f58e9ceef9118e6873f Template:Cite web 10 422 844 2016-12-05T05:36:52Z wikipedia>Anthony Appleyard 0 Protected "[[Template:Cite web]]": restore old protection ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) wikitext text/x-wiki <includeonly>{{#invoke:citation/CS1|citation |CitationClass=web }}</includeonly><noinclude> {{documentation}} </noinclude> ea1b0f38afd9728a1cf9f2e3f540887a402fab8e Template:Loop 10 466 932 2017-04-08T20:28:43Z wikipedia>Jo-Jo Eumerus 0 Changed protection level for "[[Template:Loop]]": [[WP:High-risk templates|Highly visible template]]: Allowing template editors ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:String|rep|1={{{2|}}}|2={{{1|<noinclude>1</noinclude>}}}}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go in Wikidata --> </noinclude> bbbcfd80f9d6f4a62d5861134ad1d6f2d34ff82c Template:Find sources mainspace 10 425 850 2017-05-20T18:49:40Z wikipedia>Plastikspork 0 Fix wikitext text/x-wiki {{#invoke:Find sources|Find sources mainspace}}<noinclude> {{#invoke:Find sources/autodoc|Find sources mainspace}} </noinclude> 47c0ba9d406528e894f4077dd33ec0fe489d0af2 Template:Video game release 10 460 920 2018-02-13T20:24:22Z wikipedia>WOSlinker 0 separate pp-template not needed wikitext text/x-wiki {{#invoke:Video game release|main}}<noinclude> {{Documentation}} </noinclude> b303d4187e71b065d565c75cb332c2da8f6e30bd Template:Yesno-no 10 324 643 2018-02-13T20:27:17Z wikipedia>WOSlinker 0 separate pp-template not needed wikitext text/x-wiki {{safesubst:<noinclude />yesno|{{{1}}}|yes={{{yes|yes}}}|no={{{no|no}}}|blank={{{blank|no}}}|¬={{{¬|no}}}|def={{{def|no}}}}}<noinclude> {{Documentation|Template:Yesno/doc}} <!--Categories go in the doc page referenced above; interwikis go in Wikidata.--> </noinclude> 1ad7b7800da1b867ead8f6ff8cef76e6201b3b56 Module:Distinguish 828 484 966 2018-04-01T10:06:10Z infoboxes>Galobtter 0 fixed with text and selfref Scribunto text/plain local mHatnote = require('Module:Hatnote') local mHatlist = require('Module:Hatnote list') local mArguments --initialize lazily local mTableTools --initialize lazily local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local p = {} function p.distinguish(frame) mArguments = require('Module:Arguments') mTableTools = require('Module:TableTools') local args = mArguments.getArgs(frame) local selfref = args.selfref local text = args.text args = mTableTools.compressSparseArray(args) return p._distinguish(args, text, selfref) end function p._distinguish(args, text, selfref) checkType("_distinguish", 1, args, 'table') if #args == 0 and not text then return '' end local text = string.format( 'Not to be confused with %s.', text or mHatlist.orList(args, true) ) hnOptions = {selfref = selfref} return mHatnote._hatnote(text, hnOptions) end return p 0364d14af01fc656ad1d898c5036fbd12a7ca938 Module:For loop 828 503 1008 2018-07-23T22:55:18Z string2>Pppery 0 Merge complete wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:For loop|main}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 12b7b7010fe32f888e21bcdfa6a904fc8a925437 Module:Tag 828 508 1022 2018-07-26T17:17:13Z string2>SMcCandlish 0 These are called attributes; no one who does HTML calls them "parameters". wikitext text/x-wiki <code class="{{#ifeq:{{{wrap|}}}|yes|wrap|nowrap}}" style="{{#ifeq:{{{style|}}}|plain|border:none;background:transparent;|{{{style|}}}}}"><!-- Opening tag -->{{#switch:{{{2|pair}}} |c|close = |e|empty|s|single|v|void |o|open |p|pair = &lt;{{#if:{{{link|}}}|[[HTML element#{{{1|tag}}}|{{{1|tag}}}]]|{{{1|tag}}}}}{{#if:{{{params|{{{attribs|}}}}}}|&#32;{{{params|{{{attribs}}}}}}}} }}<!-- Content between tags -->{{#switch:{{{2|pair}}} |c|close = {{{content|}}} |e|empty|s|single|v|void = &#32;&#47;&gt; |o|open = &gt;{{{content|}}} |p|pair = {{#ifeq:{{{1|tag}}}|!--||&gt;}}{{{content|...}}} }}<!-- Closing tag -->{{#switch:{{{2|pair}}} |e|empty|s|single|v|void |o|open = |c|close |p|pair = {{#ifeq:{{{1|tag}}}|!--|--&gt;|&lt;&#47;{{{1|tag}}}&gt;}} }}<!-- --></code><noinclude> {{Documentation}} </noinclude> eae208bc1612c834de697fa3ee9b343966cf8602 Template:Infobox 10 304 603 2018-08-15T18:33:36Z wikipedia>Primefac 0 Undid revision 855063393 by [[Special:Contributions/Jdlrobson|Jdlrobson]] ([[User talk:Jdlrobson|talk]]) rather problematic change mentioned [[Template_talk:Infobox#Using_template_styles_to_reduce_technical_debt_inside_mobile_skin|on talk page]], reverting until it can be sorted wikitext text/x-wiki {{#invoke:Infobox|infobox}}<includeonly>{{template other|{{#ifeq:{{PAGENAME}}|Infobox||{{#ifeq:{{str left|{{SUBPAGENAME}}|7}}|Infobox|[[Category:Infobox templates|{{remove first word|{{SUBPAGENAME}}}}]]}}}}|}}</includeonly><noinclude> {{documentation}} <!-- Categories go in the /doc subpage, and interwikis go in Wikidata. --> </noinclude> 817a9f5b6524eced06a57bd1d5fd7179f9369bf2 Template:Nobold 10 309 613 2018-10-23T17:15:13Z wikipedia>Frietjes 0 wikitext text/x-wiki <templatestyles src="Nobold/styles.css"/><span class="nobold">{{{1}}}</span><noinclude> {{documentation}} <!-- PLEASE ADD CATEGORIES AND INTERWIKIS TO THE /doc SUBPAGE, THANKS --> </noinclude> 9c92b5951772bb26ca0fbe9256418b65e47700dd Module:Format item 828 512 1030 2018-12-07T07:48:58Z string2>MusikAnimal 0 Protected "[[Template:Format item]]": [[Wikipedia:High-risk templates|High-risk template]] ([Edit=Require autoconfirmed or confirmed access] (indefinite) [Move=Require autoconfirmed or confirmed access] (indefinite)) wikitext text/x-wiki <includeonly>{{#invoke:Item|format}}</includeonly><noinclude> {{doc}} </noinclude> 0de34fe93b7513b034123ce1c532fd0f2df896dc Module:Item 828 513 1032 2018-12-07T07:48:58Z string2>MusikAnimal 0 Protected "[[Template:Item]]": [[Wikipedia:High-risk templates|High-risk template]] ([Edit=Require autoconfirmed or confirmed access] (indefinite) [Move=Require autoconfirmed or confirmed access] (indefinite)) wikitext text/x-wiki <includeonly>{{#invoke:Item|pack}}</includeonly><noinclude> {{doc}} </noinclude> 2a8d49a45be839260ad83d33fa73c804d0a39e55 1038 1032 2019-01-24T21:24:24Z string2>MusikAnimal 0 Protected "[[Module:Item]]": [[Wikipedia:High-risk templates|High-risk module]] ([Edit=Require autoconfirmed or confirmed access] (indefinite)) Scribunto text/plain local p = {} local function escape(str) return str:gsub("[|\\]", function (c) return string.format("\\%03d", c:byte()) end) end local function unescape(str) return str:gsub("\\(%d%d%d)", function (d) return string.char(d) end) end -- Implements [[Template:Item]] function p.pack(frame) local parent = frame:getParent() local result = '' for key, value in pairs(parent.args) do result = result .. "|" .. escape(tostring(key)) .. "|" .. escape(value) end return result .. "|"; end local function unpack(str) local result = { } for key, value in str:gfind("|([^|]*)|([^|]*)") do result[unescape(key)] = unescape(value) end return result end -- Implements [[Template:Component]] function p.component(frame) return unpack(frame.args[1])[frame.args[2]] end local function getItems(frame) return frame:getParent().args end local function invert(tbl) local result = { } for key, value in pairs(tbl) do result[value] = key end return result end -- Add args into item as appropriate (see [[Template:Format item]]) local function addArgs( item, -- unpacked item to modify args, -- arguments for adding into item ignore, -- pass in invert{keys to ignore} shift -- for numbered arguments, args[key+shift] is assigned to item[key] -- returns: item ) for key, value in pairs(args) do if not ignore[key] then local _, _, paramKey = string.find(key, "^param (.*)") local _, _, importantKey = string.find(key, "^important (.*)") paramKey = paramKey or importantKey or key if shift and type(paramKey) == "number" then paramKey = paramKey - shift if paramKey < 1 then paramKey = nil end end if paramKey and (importantKey or item[paramKey] == nil) then item[paramKey] = value end end end return item end -- Implements [[Template:Format item]] function p.format(frame) local args = frame:getParent().args local ignore = invert{ "template", "item" } local templateArgs = addArgs(unpack(args.item), args, ignore) return frame:expandTemplate{ title = args.template, args = templateArgs } end -- See [[Template:Item#Format each item using a template]] function p.each(frame) local args = frame.args local items = getItems(frame) local separator = args[1] or "" local prepend = args[2] or "" local append = args[3] or "" local ignore = invert{ "template" } local shift = 3 local result = "" for i, item in ipairs(items) do local templateArgs = addArgs(unpack(item), args, ignore, shift) result = result .. prepend .. frame:expandTemplate{ title = args.template, args = templateArgs } .. append if items[i + 1] then result = result .. separator end end return result end -- See [[Template:Item#Gather given parameter from all items]] function p.gather(frame) local args = frame.args local items = getItems(frame) local parameter = args.parameter or "1" local templateArgs = { } for i, item in ipairs(items) do templateArgs[i] = unpack(item)[parameter] end return frame:expandTemplate{ title = args.template, args = templateArgs } end return p 46e61b1549d18c301bcc79ecd120a0aa790f8894 Template:Template other 10 314 623 2018-12-16T22:06:25Z wikipedia>Amorymeltzer 0 Changed protection level for "[[Template:Template other]]": [[WP:High-risk templates|Highly visible template]]: Transclusion count has increased dramatically ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:Template}} | template | other }} }} | template = {{{1|}}} | other | #default = {{{2|}}} }}<!--End switch--><noinclude> {{documentation}} <!-- Add categories and interwikis to the /doc subpage, not here! --> </noinclude> 06fb13d264df967b5232141067eb7d2b67372d76 Module:Separated entries 828 500 1000 2018-12-17T20:54:33Z string2>Amorymeltzer 0 Changed protection level for "[[Module:Separated entries]]": [[WP:High-risk templates|High-risk Lua module]]: Over 2M transclusions ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) Scribunto text/plain -- This module takes positional parameters as input and concatenates them with -- an optional separator. The final separator (the "conjunction") can be -- specified independently, enabling natural-language lists like -- "foo, bar, baz and qux". The starting parameter can also be specified. local compressSparseArray = require('Module:TableTools').compressSparseArray local p = {} function p._main(args) local separator = args.separator -- Decode (convert to Unicode) HTML escape sequences, such as "&#32;" for space. and mw.text.decode(args.separator) or '' local conjunction = args.conjunction and mw.text.decode(args.conjunction) or separator -- Discard values before the starting parameter. local start = tonumber(args.start) if start then for i = 1, start - 1 do args[i] = nil end end -- Discard named parameters. local values = compressSparseArray(args) return mw.text.listToText(values, separator, conjunction) end local function makeInvokeFunction(separator, conjunction, first) return function (frame) local args = require('Module:Arguments').getArgs(frame) args.separator = separator or args.separator args.conjunction = conjunction or args.conjunction args.first = first or args.first return p._main(args) end end p.main = makeInvokeFunction() p.br = makeInvokeFunction('<br />') p.comma = makeInvokeFunction(mw.message.new('comma-separator'):plain()) return p e80231ff3de01afd7f62a94e0a34dc1e67504085 Module:WikidataIB/nolinks 828 448 896 2019-01-31T16:27:33Z wikipedia>RexxS 0 add abbreviations UK and USA Scribunto text/plain local p ={} --[[ The values here are the English sitelinks for items that should not be linked. These 36 are not definitive and may be altered to suit. --]] p.items = { "Australia", "Austria", "Belgium", "Canada", "China", "Denmark", "England", "France", "Germany", "Greece", "Hungary", "Iceland", "India", "Republic of Ireland", "Israel", "Italy", "Jamaica", "Japan", "Luxembourg", "Mexico", "Netherlands", "New Zealand", "Northern Ireland", "Norway", "Poland", "Portugal", "Russia", "Scotland", "South Africa", "Spain", "Sweden", "Switzerland", "Turkey", "United Kingdom", "UK", "United States", "USA", "Wales", } --[[ This provides a convenient way to create a test whether an item is on the list. --]] p.itemsindex = {} for i, v in ipairs(p.items) do p.itemsindex[v] = true end return p d42a1e1cb5d411ab1b578dc0d36aa0266f32b2d6 Template:Nobold/styles.css 10 310 615 2019-03-03T23:43:41Z wikipedia>Pppery 0 Adding protection template text text/plain /* {{pp-template}} */ /* Styling for Template:Nobold */ .nobold { font-weight: normal; } 83e5f0adacf8c7984251f1fd9d11ed82ebaadf03 Template:Cite magazine 10 434 868 2019-03-05T14:21:58Z wikipedia>Xaosflux 0 Changed protection level for "[[Template:Cite magazine]]": Pages alread protected from [[Wikipedia:Cascade-protected items/content]] ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) wikitext text/x-wiki <includeonly>{{#invoke:Citation/CS1|citation |CitationClass=magazine }}</includeonly><noinclude> {{documentation}} </noinclude> 9e3a497b787ae19d8cb5e2085b21bb4bfc02eee0 Template:Lua 10 335 665 2019-03-20T22:04:45Z wikipedia>RMCD bot 0 Removing notice of move discussion wikitext text/x-wiki <includeonly>{{#invoke:Lua banner|main}}</includeonly><noinclude> {{Lua|Module:Lua banner}} {{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> dba3962144dacd289dbc34f50fbe0a7bf6d7f2f7 Module:Lua 828 367 728 2019-03-20T22:04:45Z wikipedia>RMCD bot 0 Removing notice of move discussion wikitext text/x-wiki <includeonly>{{#invoke:Lua banner|main}}</includeonly><noinclude> {{Lua|Module:Lua banner}} {{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> dba3962144dacd289dbc34f50fbe0a7bf6d7f2f7 Template:Clc 10 377 750 2019-04-24T04:30:59Z wikipedia>JJMC89 0 actual template is in the category wikitext text/x-wiki #REDIRECT [[Template:Category link with count]] 02280e2ab57b544236e11f913e3759c5781ca9d5 Module:Clc 828 384 768 2019-04-24T04:30:59Z wikipedia>JJMC89 0 actual template is in the category wikitext text/x-wiki #REDIRECT [[Template:Category link with count]] 02280e2ab57b544236e11f913e3759c5781ca9d5 Module:Carousel 828 525 1058 2019-05-30T23:04:05Z string2>RexxS 0 trim the unnamed parameter Scribunto text/plain p = {} -- carousel returns one of a list of image filenames -- -- the index of the one chosen increments every 'switchsecs' -- which is a parameter giving the number of seconds between switches -- 3600 would switch every hour -- 43200 would be every 12 hours -- 86400 would be daily (the default) -- -- The list of filenames is in a named submodule, so everyone can have their own list. -- For Komodobish (the default), the module is [[Module:Carousel/Komodobish]]. -- For Serial Number 54129, the module is [[Module:Carousel/54129]]. -- See https://en.wikipedia.org/wiki/Special:PrefixIndex/Module:Carousel/ -- -- {{#invoke:carousel | main | name = name-of-datamodule | switchsecs = number-of-seconds }} -- {{#invoke:carousel | main | name = 54129 | switchsecs = 10 }} for 10 sec switches using [[Module:Carousel/54129]] -- {{#invoke:carousel | main }} for 24 hours between switches using the default data module -- p.main = function(frame) -- get parameter switchsecs; if NaN or less than 1, set default local switchtime = tonumber(frame.args.switchsecs) or 86400 if switchtime < 1 then switchtime = 86400 end -- get parameter dataname; if missing, use default local dataname = frame.args.name or mw.text.trim(frame.args[1]) or "" if dataname == "" then dataname = "Komodobish" end -- there should be a named data module as a submodule local imgs = require("Module:Carousel/" .. dataname) local numimgs = #imgs -- 'now' increments by 1 every switchtime seconds local now = math.floor( os.time() / switchtime ) -- set an index between 1 and number of images local idx = now % numimgs + 1 return imgs[idx] end return p c6d1e3326a071126a6efd72815b2b21a3ede9a93 Module:Carousel/WPDogs 828 526 1060 2019-08-31T15:26:10Z string2>Atsme 0 remove dab link Scribunto text/plain -- Module:Carousel/WPDogs -- Filename table for use in Module:Carousel -- Add filenames without the File: stuff, just the name -- Each filename goes on a new line surrounded by " -- (or by ' if there happens to be "" inside the filename -- if there's both " and ' give up on using that or rename it) -- Each line has a comma at the end. -- This one returns a caption as well - write "Filename | Caption" below: local i = { "YellowLabradorLooking new.jpg | Yellow [[Labrador Retriever]]", "Dobermann Fond Blanc.jpg | Female [[Dobermann]] with docked tail", "סאטף אנגלי.jpg | [[Staffordshire Bull Terrier]]", "Racibórz 2007 082.jpg | [[English Bulldog]], Racibórz, Poland", "Dalmatian liver stacked.jpg | [[Dalmatian dog]] stacked for show", "(2)BIR Grupp 7- KORTHÅRIG VORSTEH, J Björnkärrets Hertzogin Aida (24208119306).jpg|[[German Shorthaired Pointer]]", } return i 78c1e4260c7e8be175f81bd9d629fa90574476e1 Module:Ombox 828 359 712 2020-04-01T06:12:36Z wikipedia>MusikAnimal 0 1 revision imported wikitext text/x-wiki {{#invoke:Message box|ombox}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 0e54065432d540737b9e56c4e3a8e7f74d4534ea Template:Ombox 10 376 748 2020-04-01T06:12:36Z wikipedia>MusikAnimal 0 1 revision imported wikitext text/x-wiki {{#invoke:Message box|ombox}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 0e54065432d540737b9e56c4e3a8e7f74d4534ea Module:Arguments 828 346 686 2020-04-01T06:12:40Z wikipedia>MusikAnimal 0 1 revision imported Scribunto text/plain -- This module provides easy processing of arguments passed to Scribunto from -- #invoke. It is intended for use by other Lua modules, and should not be -- called from #invoke directly. local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local arguments = {} -- Generate four different tidyVal functions, so that we don't have to check the -- options every time we call it. local function tidyValDefault(key, val) if type(val) == 'string' then val = val:match('^%s*(.-)%s*$') if val == '' then return nil else return val end else return val end end local function tidyValTrimOnly(key, val) if type(val) == 'string' then return val:match('^%s*(.-)%s*$') else return val end end local function tidyValRemoveBlanksOnly(key, val) if type(val) == 'string' then if val:find('%S') then return val else return nil end else return val end end local function tidyValNoChange(key, val) return val end local function matchesTitle(given, title) local tp = type( given ) return (tp == 'string' or tp == 'number') and mw.title.new( given ).prefixedText == title end local translate_mt = { __index = function(t, k) return k end } function arguments.getArgs(frame, options) checkType('getArgs', 1, frame, 'table', true) checkType('getArgs', 2, options, 'table', true) frame = frame or {} options = options or {} --[[ -- Set up argument translation. --]] options.translate = options.translate or {} if getmetatable(options.translate) == nil then setmetatable(options.translate, translate_mt) end if options.backtranslate == nil then options.backtranslate = {} for k,v in pairs(options.translate) do options.backtranslate[v] = k end end if options.backtranslate and getmetatable(options.backtranslate) == nil then setmetatable(options.backtranslate, { __index = function(t, k) if options.translate[k] ~= k then return nil else return k end end }) end --[[ -- Get the argument tables. If we were passed a valid frame object, get the -- frame arguments (fargs) and the parent frame arguments (pargs), depending -- on the options set and on the parent frame's availability. If we weren't -- passed a valid frame object, we are being called from another Lua module -- or from the debug console, so assume that we were passed a table of args -- directly, and assign it to a new variable (luaArgs). --]] local fargs, pargs, luaArgs if type(frame.args) == 'table' and type(frame.getParent) == 'function' then if options.wrappers then --[[ -- The wrappers option makes Module:Arguments look up arguments in -- either the frame argument table or the parent argument table, but -- not both. This means that users can use either the #invoke syntax -- or a wrapper template without the loss of performance associated -- with looking arguments up in both the frame and the parent frame. -- Module:Arguments will look up arguments in the parent frame -- if it finds the parent frame's title in options.wrapper; -- otherwise it will look up arguments in the frame object passed -- to getArgs. --]] local parent = frame:getParent() if not parent then fargs = frame.args else local title = parent:getTitle():gsub('/sandbox$', '') local found = false if matchesTitle(options.wrappers, title) then found = true elseif type(options.wrappers) == 'table' then for _,v in pairs(options.wrappers) do if matchesTitle(v, title) then found = true break end end end -- We test for false specifically here so that nil (the default) acts like true. if found or options.frameOnly == false then pargs = parent.args end if not found or options.parentOnly == false then fargs = frame.args end end else -- options.wrapper isn't set, so check the other options. if not options.parentOnly then fargs = frame.args end if not options.frameOnly then local parent = frame:getParent() pargs = parent and parent.args or nil end end if options.parentFirst then fargs, pargs = pargs, fargs end else luaArgs = frame end -- Set the order of precedence of the argument tables. If the variables are -- nil, nothing will be added to the table, which is how we avoid clashes -- between the frame/parent args and the Lua args. local argTables = {fargs} argTables[#argTables + 1] = pargs argTables[#argTables + 1] = luaArgs --[[ -- Generate the tidyVal function. If it has been specified by the user, we -- use that; if not, we choose one of four functions depending on the -- options chosen. This is so that we don't have to call the options table -- every time the function is called. --]] local tidyVal = options.valueFunc if tidyVal then if type(tidyVal) ~= 'function' then error( "bad value assigned to option 'valueFunc'" .. '(function expected, got ' .. type(tidyVal) .. ')', 2 ) end elseif options.trim ~= false then if options.removeBlanks ~= false then tidyVal = tidyValDefault else tidyVal = tidyValTrimOnly end else if options.removeBlanks ~= false then tidyVal = tidyValRemoveBlanksOnly else tidyVal = tidyValNoChange end end --[[ -- Set up the args, metaArgs and nilArgs tables. args will be the one -- accessed from functions, and metaArgs will hold the actual arguments. Nil -- arguments are memoized in nilArgs, and the metatable connects all of them -- together. --]] local args, metaArgs, nilArgs, metatable = {}, {}, {}, {} setmetatable(args, metatable) local function mergeArgs(tables) --[[ -- Accepts multiple tables as input and merges their keys and values -- into one table. If a value is already present it is not overwritten; -- tables listed earlier have precedence. We are also memoizing nil -- values, which can be overwritten if they are 's' (soft). --]] for _, t in ipairs(tables) do for key, val in pairs(t) do if metaArgs[key] == nil and nilArgs[key] ~= 'h' then local tidiedVal = tidyVal(key, val) if tidiedVal == nil then nilArgs[key] = 's' else metaArgs[key] = tidiedVal end end end end end --[[ -- Define metatable behaviour. Arguments are memoized in the metaArgs table, -- and are only fetched from the argument tables once. Fetching arguments -- from the argument tables is the most resource-intensive step in this -- module, so we try and avoid it where possible. For this reason, nil -- arguments are also memoized, in the nilArgs table. Also, we keep a record -- in the metatable of when pairs and ipairs have been called, so we do not -- run pairs and ipairs on the argument tables more than once. We also do -- not run ipairs on fargs and pargs if pairs has already been run, as all -- the arguments will already have been copied over. --]] metatable.__index = function (t, key) --[[ -- Fetches an argument when the args table is indexed. First we check -- to see if the value is memoized, and if not we try and fetch it from -- the argument tables. When we check memoization, we need to check -- metaArgs before nilArgs, as both can be non-nil at the same time. -- If the argument is not present in metaArgs, we also check whether -- pairs has been run yet. If pairs has already been run, we return nil. -- This is because all the arguments will have already been copied into -- metaArgs by the mergeArgs function, meaning that any other arguments -- must be nil. --]] if type(key) == 'string' then key = options.translate[key] end local val = metaArgs[key] if val ~= nil then return val elseif metatable.donePairs or nilArgs[key] then return nil end for _, argTable in ipairs(argTables) do local argTableVal = tidyVal(key, argTable[key]) if argTableVal ~= nil then metaArgs[key] = argTableVal return argTableVal end end nilArgs[key] = 'h' return nil end metatable.__newindex = function (t, key, val) -- This function is called when a module tries to add a new value to the -- args table, or tries to change an existing value. if type(key) == 'string' then key = options.translate[key] end if options.readOnly then error( 'could not write to argument table key "' .. tostring(key) .. '"; the table is read-only', 2 ) elseif options.noOverwrite and args[key] ~= nil then error( 'could not write to argument table key "' .. tostring(key) .. '"; overwriting existing arguments is not permitted', 2 ) elseif val == nil then --[[ -- If the argument is to be overwritten with nil, we need to erase -- the value in metaArgs, so that __index, __pairs and __ipairs do -- not use a previous existing value, if present; and we also need -- to memoize the nil in nilArgs, so that the value isn't looked -- up in the argument tables if it is accessed again. --]] metaArgs[key] = nil nilArgs[key] = 'h' else metaArgs[key] = val end end local function translatenext(invariant) local k, v = next(invariant.t, invariant.k) invariant.k = k if k == nil then return nil elseif type(k) ~= 'string' or not options.backtranslate then return k, v else local backtranslate = options.backtranslate[k] if backtranslate == nil then -- Skip this one. This is a tail call, so this won't cause stack overflow return translatenext(invariant) else return backtranslate, v end end end metatable.__pairs = function () -- Called when pairs is run on the args table. if not metatable.donePairs then mergeArgs(argTables) metatable.donePairs = true end return translatenext, { t = metaArgs } end local function inext(t, i) -- This uses our __index metamethod local v = t[i + 1] if v ~= nil then return i + 1, v end end metatable.__ipairs = function (t) -- Called when ipairs is run on the args table. return inext, t, 0 end return args end return arguments 3134ecce8429b810d445e29eae115e2ae4c36c53 Module:Category handler 828 417 834 2020-04-01T06:12:40Z wikipedia>MusikAnimal 0 1 revision imported Scribunto text/plain -------------------------------------------------------------------------------- -- -- -- CATEGORY HANDLER -- -- -- -- This module implements the {{category handler}} template in Lua, -- -- with a few improvements: all namespaces and all namespace aliases -- -- are supported, and namespace names are detected automatically for -- -- the local wiki. This module requires [[Module:Namespace detect]] -- -- and [[Module:Yesno]] to be available on the local wiki. It can be -- -- configured for different wikis by altering the values in -- -- [[Module:Category handler/config]], and pages can be blacklisted -- -- from categorisation by using [[Module:Category handler/blacklist]]. -- -- -- -------------------------------------------------------------------------------- -- Load required modules local yesno = require('Module:Yesno') -- Lazily load things we don't always need local mShared, mappings local p = {} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function trimWhitespace(s, removeBlanks) if type(s) ~= 'string' then return s end s = s:match('^%s*(.-)%s*$') if removeBlanks then if s ~= '' then return s else return nil end else return s end end -------------------------------------------------------------------------------- -- CategoryHandler class -------------------------------------------------------------------------------- local CategoryHandler = {} CategoryHandler.__index = CategoryHandler function CategoryHandler.new(data, args) local obj = setmetatable({ _data = data, _args = args }, CategoryHandler) -- Set the title object do local pagename = obj:parameter('demopage') local success, titleObj if pagename then success, titleObj = pcall(mw.title.new, pagename) end if success and titleObj then obj.title = titleObj if titleObj == mw.title.getCurrentTitle() then obj._usesCurrentTitle = true end else obj.title = mw.title.getCurrentTitle() obj._usesCurrentTitle = true end end -- Set suppression parameter values for _, key in ipairs{'nocat', 'categories'} do local value = obj:parameter(key) value = trimWhitespace(value, true) obj['_' .. key] = yesno(value) end do local subpage = obj:parameter('subpage') local category2 = obj:parameter('category2') if type(subpage) == 'string' then subpage = mw.ustring.lower(subpage) end if type(category2) == 'string' then subpage = mw.ustring.lower(category2) end obj._subpage = trimWhitespace(subpage, true) obj._category2 = trimWhitespace(category2) -- don't remove blank values end return obj end function CategoryHandler:parameter(key) local parameterNames = self._data.parameters[key] local pntype = type(parameterNames) if pntype == 'string' or pntype == 'number' then return self._args[parameterNames] elseif pntype == 'table' then for _, name in ipairs(parameterNames) do local value = self._args[name] if value ~= nil then return value end end return nil else error(string.format( 'invalid config key "%s"', tostring(key) ), 2) end end function CategoryHandler:isSuppressedByArguments() return -- See if a category suppression argument has been set. self._nocat == true or self._categories == false or ( self._category2 and self._category2 ~= self._data.category2Yes and self._category2 ~= self._data.category2Negative ) -- Check whether we are on a subpage, and see if categories are -- suppressed based on our subpage status. or self._subpage == self._data.subpageNo and self.title.isSubpage or self._subpage == self._data.subpageOnly and not self.title.isSubpage end function CategoryHandler:shouldSkipBlacklistCheck() -- Check whether the category suppression arguments indicate we -- should skip the blacklist check. return self._nocat == false or self._categories == true or self._category2 == self._data.category2Yes end function CategoryHandler:matchesBlacklist() if self._usesCurrentTitle then return self._data.currentTitleMatchesBlacklist else mShared = mShared or require('Module:Category handler/shared') return mShared.matchesBlacklist( self.title.prefixedText, mw.loadData('Module:Category handler/blacklist') ) end end function CategoryHandler:isSuppressed() -- Find if categories are suppressed by either the arguments or by -- matching the blacklist. return self:isSuppressedByArguments() or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist() end function CategoryHandler:getNamespaceParameters() if self._usesCurrentTitle then return self._data.currentTitleNamespaceParameters else if not mappings then mShared = mShared or require('Module:Category handler/shared') mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData end return mShared.getNamespaceParameters( self.title, mappings ) end end function CategoryHandler:namespaceParametersExist() -- Find whether any namespace parameters have been specified. -- We use the order "all" --> namespace params --> "other" as this is what -- the old template did. if self:parameter('all') then return true end if not mappings then mShared = mShared or require('Module:Category handler/shared') mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData end for ns, params in pairs(mappings) do for i, param in ipairs(params) do if self._args[param] then return true end end end if self:parameter('other') then return true end return false end function CategoryHandler:getCategories() local params = self:getNamespaceParameters() local nsCategory for i, param in ipairs(params) do local value = self._args[param] if value ~= nil then nsCategory = value break end end if nsCategory ~= nil or self:namespaceParametersExist() then -- Namespace parameters exist - advanced usage. if nsCategory == nil then nsCategory = self:parameter('other') end local ret = {self:parameter('all')} local numParam = tonumber(nsCategory) if numParam and numParam >= 1 and math.floor(numParam) == numParam then -- nsCategory is an integer ret[#ret + 1] = self._args[numParam] else ret[#ret + 1] = nsCategory end if #ret < 1 then return nil else return table.concat(ret) end elseif self._data.defaultNamespaces[self.title.namespace] then -- Namespace parameters don't exist, simple usage. return self._args[1] end return nil end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p = {} function p._exportClasses() -- Used for testing purposes. return { CategoryHandler = CategoryHandler } end function p._main(args, data) data = data or mw.loadData('Module:Category handler/data') local handler = CategoryHandler.new(data, args) if handler:isSuppressed() then return nil end return handler:getCategories() end function p.main(frame, data) data = data or mw.loadData('Module:Category handler/data') local args = require('Module:Arguments').getArgs(frame, { wrappers = data.wrappers, valueFunc = function (k, v) v = trimWhitespace(v) if type(k) == 'number' then if v ~= '' then return v else return nil end else return v end end }) return p._main(args, data) end return p b74dd63857b24904ac452429b11213f18647471f Module:Category handler/config 828 419 838 2020-04-01T06:12:40Z wikipedia>MusikAnimal 0 1 revision imported Scribunto text/plain -------------------------------------------------------------------------------- -- [[Module:Category handler]] configuration data -- -- Language-specific parameter names and values can be set here. -- -- For blacklist config, see [[Module:Category handler/blacklist]]. -- -------------------------------------------------------------------------------- local cfg = {} -- Don't edit this line. -------------------------------------------------------------------------------- -- Start configuration data -- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- Parameter names -- -- These configuration items specify custom parameter names. -- -- To add one extra name, you can use this format: -- -- -- -- foo = 'parameter name', -- -- -- -- To add multiple names, you can use this format: -- -- -- -- foo = {'parameter name 1', 'parameter name 2', 'parameter name 3'}, -- -------------------------------------------------------------------------------- cfg.parameters = { -- The nocat and categories parameter suppress -- categorisation. They are used with Module:Yesno, and work as follows: -- -- cfg.nocat: -- Result of yesno() Effect -- true Categorisation is suppressed -- false Categorisation is allowed, and -- the blacklist check is skipped -- nil Categorisation is allowed -- -- cfg.categories: -- Result of yesno() Effect -- true Categorisation is allowed, and -- the blacklist check is skipped -- false Categorisation is suppressed -- nil Categorisation is allowed nocat = 'nocat', categories = 'categories', -- The parameter name for the legacy "category2" parameter. This skips the -- blacklist if set to the cfg.category2Yes value, and suppresses -- categorisation if present but equal to anything other than -- cfg.category2Yes or cfg.category2Negative. category2 = 'category2', -- cfg.subpage is the parameter name to specify how to behave on subpages. subpage = 'subpage', -- The parameter for data to return in all namespaces. all = 'all', -- The parameter name for data to return if no data is specified for the -- namespace that is detected. other = 'other', -- The parameter name used to specify a page other than the current page; -- used for testing and demonstration. demopage = 'page', } -------------------------------------------------------------------------------- -- Parameter values -- -- These are set values that can be used with certain parameters. Only one -- -- value can be specified, like this: -- -- -- -- cfg.foo = 'value name' -- -- -------------------------------------------------------------------------------- -- The following settings are used with the cfg.category2 parameter. Setting -- cfg.category2 to cfg.category2Yes skips the blacklist, and if cfg.category2 -- is present but equal to anything other than cfg.category2Yes or -- cfg.category2Negative then it supresses cateogrisation. cfg.category2Yes = 'yes' cfg.category2Negative = '¬' -- The following settings are used with the cfg.subpage parameter. -- cfg.subpageNo is the value to specify to not categorise on subpages; -- cfg.subpageOnly is the value to specify to only categorise on subpages. cfg.subpageNo = 'no' cfg.subpageOnly = 'only' -------------------------------------------------------------------------------- -- Default namespaces -- -- This is a table of namespaces to categorise by default. The keys are the -- -- namespace numbers. -- -------------------------------------------------------------------------------- cfg.defaultNamespaces = { [ 0] = true, -- main [ 6] = true, -- file [ 12] = true, -- help [ 14] = true, -- category [100] = true, -- portal [108] = true, -- book } -------------------------------------------------------------------------------- -- Wrappers -- -- This is a wrapper template or a list of wrapper templates to be passed to -- -- [[Module:Arguments]]. -- -------------------------------------------------------------------------------- cfg.wrappers = 'Template:Category handler' -------------------------------------------------------------------------------- -- End configuration data -- -------------------------------------------------------------------------------- return cfg -- Don't edit this line. 373cd107b13a5b00e6a1b7e66a749f12502c849d Module:Category handler/data 828 418 836 2020-04-01T06:12:41Z wikipedia>MusikAnimal 0 1 revision imported Scribunto text/plain -- This module assembles data to be passed to [[Module:Category handler]] using -- mw.loadData. This includes the configuration data and whether the current -- page matches the title blacklist. local data = require('Module:Category handler/config') local mShared = require('Module:Category handler/shared') local blacklist = require('Module:Category handler/blacklist') local title = mw.title.getCurrentTitle() data.currentTitleMatchesBlacklist = mShared.matchesBlacklist( title.prefixedText, blacklist ) data.currentTitleNamespaceParameters = mShared.getNamespaceParameters( title, mShared.getParamMappings() ) return data abbc68048ff698e88dda06b64ecf384bbf583120 Module:Category handler/shared 828 420 840 2020-04-01T06:12:41Z wikipedia>MusikAnimal 0 1 revision imported Scribunto text/plain -- This module contains shared functions used by [[Module:Category handler]] -- and its submodules. local p = {} function p.matchesBlacklist(page, blacklist) for i, pattern in ipairs(blacklist) do local match = mw.ustring.match(page, pattern) if match then return true end end return false end function p.getParamMappings(useLoadData) local dataPage = 'Module:Namespace detect/data' if useLoadData then return mw.loadData(dataPage).mappings else return require(dataPage).mappings end end function p.getNamespaceParameters(titleObj, mappings) -- We don't use title.nsText for the namespace name because it adds -- underscores. local mappingsKey if titleObj.isTalkPage then mappingsKey = 'talk' else mappingsKey = mw.site.namespaces[titleObj.namespace].name end mappingsKey = mw.ustring.lower(mappingsKey) return mappings[mappingsKey] or {} end return p d2d5de1a031e6ce97c242cbfa8afe7a92cb9eca5 Module:Namespace detect/config 828 410 820 2020-04-01T06:12:44Z wikipedia>MusikAnimal 0 1 revision imported Scribunto text/plain -------------------------------------------------------------------------------- -- Namespace detect configuration data -- -- -- -- This module stores configuration data for Module:Namespace detect. Here -- -- you can localise the module to your wiki's language. -- -- -- -- To activate a configuration item, you need to uncomment it. This means -- -- that you need to remove the text "-- " at the start of the line. -- -------------------------------------------------------------------------------- local cfg = {} -- Don't edit this line. -------------------------------------------------------------------------------- -- Parameter names -- -- These configuration items specify custom parameter names. Values added -- -- here will work in addition to the default English parameter names. -- -- To add one extra name, you can use this format: -- -- -- -- cfg.foo = 'parameter name' -- -- -- -- To add multiple names, you can use this format: -- -- -- -- cfg.foo = {'parameter name 1', 'parameter name 2', 'parameter name 3'} -- -------------------------------------------------------------------------------- ---- This parameter displays content for the main namespace: -- cfg.main = 'main' ---- This parameter displays in talk namespaces: -- cfg.talk = 'talk' ---- This parameter displays content for "other" namespaces (namespaces for which ---- parameters have not been specified): -- cfg.other = 'other' ---- This parameter makes talk pages behave as though they are the corresponding ---- subject namespace. Note that this parameter is used with [[Module:Yesno]]. ---- Edit that module to change the default values of "yes", "no", etc. -- cfg.subjectns = 'subjectns' ---- This parameter sets a demonstration namespace: -- cfg.demospace = 'demospace' ---- This parameter sets a specific page to compare: cfg.demopage = 'page' -------------------------------------------------------------------------------- -- Table configuration -- -- These configuration items allow customisation of the "table" function, -- -- used to generate a table of possible parameters in the module -- -- documentation. -- -------------------------------------------------------------------------------- ---- The header for the namespace column in the wikitable containing the list of ---- possible subject-space parameters. -- cfg.wikitableNamespaceHeader = 'Namespace' ---- The header for the wikitable containing the list of possible subject-space ---- parameters. -- cfg.wikitableAliasesHeader = 'Aliases' -------------------------------------------------------------------------------- -- End of configuration data -- -------------------------------------------------------------------------------- return cfg -- Don't edit this line. 0e4ff08d13c4b664d66b32c232deb129b77c1a56 Module:Namespace detect/data 828 409 818 2020-04-01T06:12:45Z wikipedia>MusikAnimal 0 1 revision imported Scribunto text/plain -------------------------------------------------------------------------------- -- Namespace detect data -- -- This module holds data for [[Module:Namespace detect]] to be loaded per -- -- page, rather than per #invoke, for performance reasons. -- -------------------------------------------------------------------------------- local cfg = require('Module:Namespace detect/config') local function addKey(t, key, defaultKey) if key ~= defaultKey then t[#t + 1] = key end end -- Get a table of parameters to query for each default parameter name. -- This allows wikis to customise parameter names in the cfg table while -- ensuring that default parameter names will always work. The cfg table -- values can be added as a string, or as an array of strings. local defaultKeys = { 'main', 'talk', 'other', 'subjectns', 'demospace', 'demopage' } local argKeys = {} for i, defaultKey in ipairs(defaultKeys) do argKeys[defaultKey] = {defaultKey} end for defaultKey, t in pairs(argKeys) do local cfgValue = cfg[defaultKey] local cfgValueType = type(cfgValue) if cfgValueType == 'string' then addKey(t, cfgValue, defaultKey) elseif cfgValueType == 'table' then for i, key in ipairs(cfgValue) do addKey(t, key, defaultKey) end end cfg[defaultKey] = nil -- Free the cfg value as we don't need it any more. end local function getParamMappings() --[[ -- Returns a table of how parameter names map to namespace names. The keys -- are the actual namespace names, in lower case, and the values are the -- possible parameter names for that namespace, also in lower case. The -- table entries are structured like this: -- { -- [''] = {'main'}, -- ['wikipedia'] = {'wikipedia', 'project', 'wp'}, -- ... -- } --]] local mappings = {} local mainNsName = mw.site.subjectNamespaces[0].name mainNsName = mw.ustring.lower(mainNsName) mappings[mainNsName] = mw.clone(argKeys.main) mappings['talk'] = mw.clone(argKeys.talk) for nsid, ns in pairs(mw.site.subjectNamespaces) do if nsid ~= 0 then -- Exclude main namespace. local nsname = mw.ustring.lower(ns.name) local canonicalName = mw.ustring.lower(ns.canonicalName) mappings[nsname] = {nsname} if canonicalName ~= nsname then table.insert(mappings[nsname], canonicalName) end for _, alias in ipairs(ns.aliases) do table.insert(mappings[nsname], mw.ustring.lower(alias)) end end end return mappings end return { argKeys = argKeys, cfg = cfg, mappings = getParamMappings() } d224f42a258bc308ef3ad8cc8686cd7a4f47d005 Module:Yesno 828 345 684 2020-04-01T06:27:55Z wikipedia>MusikAnimal 0 Undid revision 948472533 by [[Special:Contributions/w>Vogone|w>Vogone]] ([[User talk:w>Vogone|talk]]) Scribunto text/plain -- Function allowing for consistent treatment of boolean-like wikitext input. -- It works similarly to the template {{yesno}}. return function (val, default) -- If your wiki uses non-ascii characters for any of "yes", "no", etc., you -- should replace "val:lower()" with "mw.ustring.lower(val)" in the -- following line. val = type(val) == 'string' and val:lower() or val if val == nil then return nil elseif val == true or val == 'yes' or val == 'y' or val == 'true' or val == 't' or val == 'on' or tonumber(val) == 1 then return true elseif val == false or val == 'no' or val == 'n' or val == 'false' or val == 'f' or val == 'off' or tonumber(val) == 0 then return false else return default end end f767643e7d12126d020d88d662a3dd057817b9dc Module:File link 828 356 706 2020-04-01T06:31:54Z wikipedia>MusikAnimal 0 Undid revision 948472508 by [[Special:Contributions/w>IPad365|w>IPad365]] ([[User talk:w>IPad365|talk]]) Scribunto text/plain -- This module provides a library for formatting file wikilinks. local yesno = require('Module:Yesno') local checkType = require('libraryUtil').checkType local p = {} function p._main(args) checkType('_main', 1, args, 'table') -- This is basically libraryUtil.checkTypeForNamedArg, but we are rolling our -- own function to get the right error level. local function checkArg(key, val, level) if type(val) ~= 'string' then error(string.format( "type error in '%s' parameter of '_main' (expected string, got %s)", key, type(val) ), level) end end local ret = {} -- Adds a positional parameter to the buffer. local function addPositional(key) local val = args[key] if not val then return nil end checkArg(key, val, 4) ret[#ret + 1] = val end -- Adds a named parameter to the buffer. We assume that the parameter name -- is the same as the argument key. local function addNamed(key) local val = args[key] if not val then return nil end checkArg(key, val, 4) ret[#ret + 1] = key .. '=' .. val end -- Filename checkArg('file', args.file, 3) ret[#ret + 1] = 'File:' .. args.file -- Format if args.format then checkArg('format', args.format) if args.formatfile then checkArg('formatfile', args.formatfile) ret[#ret + 1] = args.format .. '=' .. args.formatfile else ret[#ret + 1] = args.format end end -- Border if yesno(args.border) then ret[#ret + 1] = 'border' end addPositional('location') addPositional('alignment') addPositional('size') addNamed('upright') addNamed('link') addNamed('alt') addNamed('page') addNamed('class') addNamed('lang') addNamed('start') addNamed('end') addNamed('thumbtime') addPositional('caption') return string.format('[[%s]]', table.concat(ret, '|')) end function p.main(frame) local origArgs = require('Module:Arguments').getArgs(frame, { wrappers = 'Template:File link' }) if not origArgs.file then error("'file' parameter missing from [[Template:File link]]", 0) end -- Copy the arguments that were passed to a new table to avoid looking up -- every possible parameter in the frame object. local args = {} for k, v in pairs(origArgs) do -- Make _BLANK a special argument to add a blank parameter. For use in -- conditional templates etc. it is useful for blank arguments to be -- ignored, but we still need a way to specify them so that we can do -- things like [[File:Example.png|link=]]. if v == '_BLANK' then v = '' end args[k] = v end return p._main(args) end return p 66925f088d11530f2482f04181a3baaaa0ad3d0c Template:Sandbox other 10 328 651 2020-04-03T00:08:09Z wikipedia>Evad37 0 Also match subpage names beginning with "sandbox", per [[Template_talk:Sandbox_other#Template-protected_edit_request_on_28_March_2020|edit request]] wikitext text/x-wiki {{#if:{{#ifeq:{{#invoke:String|sublength|s={{SUBPAGENAME}}|i=0|len=7}}|sandbox|1}}{{#ifeq:{{SUBPAGENAME}}|doc|1}}{{#invoke:String|match|{{PAGENAME}}|/sandbox/styles.css$|plain=false|nomatch=}}|{{{1|}}}|{{{2|}}}}}<!-- --><noinclude>{{documentation}}</noinclude> 91e4ae891d6b791615152c1fbc971414961ba872 Module:Sandbox other 828 363 720 2020-04-03T00:08:09Z wikipedia>Evad37 0 Also match subpage names beginning with "sandbox", per [[Template_talk:Sandbox_other#Template-protected_edit_request_on_28_March_2020|edit request]] wikitext text/x-wiki {{#if:{{#ifeq:{{#invoke:String|sublength|s={{SUBPAGENAME}}|i=0|len=7}}|sandbox|1}}{{#ifeq:{{SUBPAGENAME}}|doc|1}}{{#invoke:String|match|{{PAGENAME}}|/sandbox/styles.css$|plain=false|nomatch=}}|{{{1|}}}|{{{2|}}}}}<!-- --><noinclude>{{documentation}}</noinclude> 91e4ae891d6b791615152c1fbc971414961ba872 Template:Tlf 10 331 657 2020-04-13T14:42:57Z wikipedia>Primefac 0 Primefac moved page [[Template:Tlf]] to [[Template:Template link with link off]]: full name to indicate what it does wikitext text/x-wiki #REDIRECT [[Template:Template link with link off]] {{Redirect category shell| {{R from move}} }} 52759e1d3f7c9aa4a03d0b7d4f84f4c6adf53edf Module:Category handler/blacklist 828 421 842 2020-04-18T08:48:05Z wikipedia>Jo-Jo Eumerus 0 Per category talk page Scribunto text/plain -- This module contains the blacklist used by [[Module:Category handler]]. -- Pages that match Lua patterns in this list will not be categorised unless -- categorisation is explicitly requested. return { '^Main Page$', -- don't categorise the main page. -- Don't categorise the following pages or their subpages. -- "%f[/\0]" matches if the next character is "/" or the end of the string. '^Wikipedia:Cascade%-protected items%f[/\0]', '^User:UBX%f[/\0]', -- The userbox "template" space. '^User talk:UBX%f[/\0]', -- Don't categorise subpages of these pages, but allow -- categorisation of the base page. '^Wikipedia:Template index/.*$', -- Don't categorise archives. '/[aA]rchive', "^Wikipedia:Administrators' noticeboard/IncidentArchive%d+$", } 87469d7a9ef2a3c41b2bf04ae18f7c59a18fb855 Module:One2a 828 517 1042 2020-04-18T20:37:43Z string2>RexxS 0 Creating Template:One2a wikitext text/x-wiki {{#invoke:String2 |one2a}}<noinclude> {{documentation}} </noinclude> bc03f614684eff759042f705fc94764db994e860 Template:If first display both 10 457 914 2020-04-27T01:18:29Z wikipedia>Plastikspork 0 Nested noinclude tags wikitext text/x-wiki {{#if:{{{1|}}}|{{{1|}}}{{{2|}}}}}<noinclude> {{documentation}} </noinclude> 4d500383f7b4a06011f966ad5667428a065b296f Module:WikidataIB/titleformats 828 449 898 2020-04-30T16:11:19Z wikipedia>RexxS 0 add magazine Scribunto text/plain --[[ To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised, and others are quoted. This submodule lists the entity-ids used in 'instance of' (P31), which allows a module to identify the values that should be formatted. The table p.formats is indexed by entity-id, and contains the value " or '' --]] local p = {} p.italics = { "Q571", -- book "Q13593966", -- literary trilogy "Q277759", -- book series "Q2188189", -- musical work "Q11424", -- film "Q13593818", -- film trilogy "Q24856", -- film series "Q5398426", -- television series "Q482994", -- album "Q169930", -- extended play "Q1760610", -- comic book "Q7889", -- video game "Q7058673", -- video game series "Q25379", -- play "Q2743", -- musical "Q37484", -- epic poem "Q41298", -- magazine } p.quotes = { "Q207628", -- musical composition } p.size = 0 p.formats = {} for i, v in ipairs(p.italics) do p.formats[v] = "''" p.size = p.size + 1 end for i, v in ipairs(p.quotes) do p.formats[v] = '"' p.size = p.size + 1 end return p aecc52ff69e56d315f5b60dc21d02dd94a63dfea Module:For loop 828 503 1010 1008 2020-05-22T07:38:08Z string2>Johnuniq 0 require [[Module:Template invocation]] only if required (see [[Template talk:Post-nominals#Use of Module:Template_invocation]]); clean whitespace, variables Scribunto text/plain -- This module implements {{for loop}}. local getArgs = require('Module:Arguments').getArgs local yesno = require('Module:Yesno') local p = {} function p.main(frame) local args = getArgs(frame, { trim = false, removeBlanks = false }) return p._main(args) end function p._main(args) local template = args['call'] or 'void' local calltemplates = yesno(args.substall or "", true) or not mw.isSubsting() local variableParam = args.pv variableParam = tonumber(variableParam) or variableParam or 1 -- fix for positional parameters local variableValPrefix = args.prefix or '' local variableValPostfix = args.postfix or '' local sep = args[1] or '' local constantArgs = p.getConstants(args) local variableVals = p.getVariableVals(args) local result = '' local addSeparator = false; for _, v in ipairs(variableVals) do v = mw.text.trim(v) if #v > 0 or not yesno(args.skipBlanks) then if addSeparator then result = result .. sep end addSeparator = true; local targs = constantArgs targs[variableParam] = variableValPrefix .. v .. variableValPostfix if calltemplates then local output = p.callTemplate(template, targs) if #mw.text.trim(output) == 0 then addSeparator = false end result = result .. output else local makeTemplate = require('Module:Template invocation').invocation result = result .. makeTemplate(template, targs) end end end return result end function p.getConstants(args) local constantArgNums = p.getArgNums(args, 'pc', 'n') local constantArgs = {} for _, num in ipairs(constantArgNums) do local keyArg = 'pc' .. tostring(num) .. 'n' local valArg = 'pc' .. tostring(num) .. 'v' local key = args[keyArg] key = tonumber(key) or key local value = args[valArg] constantArgs[key] = value end return constantArgs end function p.getVariableVals(args) local variableVals = {} if args.start or args.stop or args.by then if args[2] then error("Both start/stop/by and numbered parameters specified") end local start = tonumber(args.start or 1) local stop = tonumber(args.stop or 1) local by = tonumber(args.by or 1) for i = start, stop, by do variableVals [#variableVals + 1] = i end else for i, v in ipairs(args) do if i ~= 1 then variableVals[i - 1] = v end end end return variableVals end function p.getArgNums(args, prefix, suffix) -- Returns a table containing the numbers of the arguments that exist -- for the specified prefix and suffix. local nums = {} local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$' for k, _ in pairs(args) do local num = tostring(k):match(pattern) if num then nums[#nums + 1] = tonumber(num) end end table.sort(nums) return nums end function p.callTemplate(template, targs) return mw.getCurrentFrame():expandTemplate{title = template, args = targs} end return p 4ed4682b1fd3fbf2bf0836b46dd19bc0363d40e2 Template:Sidebar 10 317 629 2020-06-04T02:43:13Z wikipedia>Primefac 0 TFD closed as keep ([[WP:XFDC|XFDcloser]]) wikitext text/x-wiki {{#invoke:Sidebar|sidebar}}<noinclude> {{documentation}}</noinclude> ab2498000a99daf324f656b0badd187b4a3e2b42 Module:Pagetype 828 406 812 2020-06-18T21:22:08Z wikipedia>RexxS 0 add caps parameter per talk request Scribunto text/plain -------------------------------------------------------------------------------- -- -- -- PAGETYPE -- -- -- -- This is a meta-module intended to replace {{pagetype}} and similar -- -- templates. It automatically detects namespaces, and allows for a -- -- great deal of customisation. It can easily be ported to other -- -- wikis by changing the values in the [[Module:Pagetype/config]]. -- -- -- -------------------------------------------------------------------------------- -- Load config. local cfg = mw.loadData('Module:Pagetype/config') -- Load required modules. local getArgs = require('Module:Arguments').getArgs local yesno = require('Module:Yesno') local nsDetectModule = require('Module:Namespace detect') local nsDetect = nsDetectModule._main local getParamMappings = nsDetectModule.getParamMappings local getPageObject = nsDetectModule.getPageObject local p = {} local function shallowCopy(t) -- Makes a shallow copy of a table. local ret = {} for k, v in pairs(t) do ret[k] = v end return ret end local function checkPagetypeInput(namespace, val) -- Checks to see whether we need the default value for the given namespace, -- and if so gets it from the pagetypes table. -- The yesno function returns true/false for "yes", "no", etc., and returns -- val for other input. local ret = yesno(val, val) if ret and type(ret) ~= 'string' then ret = cfg.pagetypes[namespace] end return ret end local function getPagetypeFromClass(class, param, aliasTable, default) -- Gets the pagetype from a class specified from the first positional -- parameter. param = yesno(param, param) if param ~= false then -- No check if specifically disallowed. for _, alias in ipairs(aliasTable) do if class == alias then if type(param) == 'string' then return param else return default end end end end end local function getNsDetectValue(args) -- Builds the arguments to pass to [[Module:Namespace detect]] and returns -- the result. -- Get the default values. local ndArgs = {} local defaultns = args[cfg.defaultns] if defaultns == cfg.defaultnsAll then ndArgs = shallowCopy(cfg.pagetypes) else local defaultnsArray if defaultns == cfg.defaultnsExtended then defaultnsArray = cfg.extendedNamespaces elseif defaultns == cfg.defaultnsNone then defaultnsArray = {} else defaultnsArray = cfg.defaultNamespaces end for _, namespace in ipairs(defaultnsArray) do ndArgs[namespace] = cfg.pagetypes[namespace] end end --[[ -- Add custom values passed in from the arguments. These overwrite the -- defaults. The possible argument names are fetched from -- Module:Namespace detect automatically in case new namespaces are -- added. Although we accept namespace aliases as parameters, we only pass -- the local namespace name as a parameter to Module:Namespace detect. -- This means that the "image" parameter can overwrite defaults for the -- File: namespace, which wouldn't work if we passed the parameters through -- separately. --]] local mappings = getParamMappings() for ns, paramAliases in pairs(mappings) do -- Copy the aliases table, as # doesn't work with tables returned from -- mw.loadData. paramAliases = shallowCopy(paramAliases) local paramName = paramAliases[1] -- Iterate backwards along the array so that any values for the local -- namespace names overwrite those for namespace aliases. for i = #paramAliases, 1, -1 do local paramAlias = paramAliases[i] local ndArg = checkPagetypeInput(paramAlias, args[paramAlias]) if ndArg == false then -- If any arguments are false, convert them to nil to protect -- against breakage by future changes to -- [[Module:Namespace detect]]. ndArgs[paramName] = nil elseif ndArg then ndArgs[paramName] = ndArg end end end -- Check for disambiguation-class and N/A-class pages in mainspace. if ndArgs.main then local class = args[1] if type(class) == 'string' then -- Put in lower case so e.g. "Dab" and "dab" will both match. class = mw.ustring.lower(class) end local dab = getPagetypeFromClass( class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault ) if dab then ndArgs.main = dab else local na = getPagetypeFromClass( class, args[cfg.na], cfg.naAliases, cfg.naDefault ) if na then ndArgs.main = na end end end -- If there is no talk value specified, use the corresponding subject -- namespace for talk pages. if not ndArgs.talk then ndArgs.subjectns = true end -- Add the fallback value. This can also be customised, but it cannot be -- disabled. local other = args[cfg.other] -- We will ignore true/false/nil results from yesno here, but using it -- anyway for consistency. other = yesno(other, other) if type(other) == 'string' then ndArgs.other = other else ndArgs.other = cfg.otherDefault end -- Allow custom page values. ndArgs.page = args.page return nsDetect(ndArgs) end local function detectRedirects(args) local redirect = args[cfg.redirect] -- The yesno function returns true/false for "yes", "no", etc., and returns -- redirect for other input. redirect = yesno(redirect, redirect) if redirect == false then -- Detect redirects unless they have been explicitly disallowed with -- "redirect=no" or similar. return end local pageObject = getPageObject(args.page) -- If we are using subject namespaces elsewhere, do so here as well. if pageObject and not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll then pageObject = getPageObject( pageObject.subjectNsText .. ':' .. pageObject.text ) end -- Allow custom values for redirects. if pageObject and pageObject.isRedirect then if type(redirect) == 'string' then return redirect else return cfg.redirectDefault end end end function p._main(args) local redirect = detectRedirects(args) local pagetype = "" if redirect then pagetype = redirect else pagetype = getNsDetectValue(args) end if yesno(args.plural, false) then if cfg.irregularPlurals[pagetype] then pagetype = cfg.irregularPlurals[pagetype] else pagetype = pagetype .. cfg.plural -- often 's' end end if yesno(args.caps, false) then pagetype = mw.ustring.upper(mw.ustring.sub(pagetype, 1, 1)) .. mw.ustring.sub(pagetype, 2) end return pagetype end function p.main(frame) local args = getArgs(frame) return p._main(args) end return p 210524e0c60e3354325aea88c508e94423ad228d Template:MobyGames 10 473 946 2020-07-26T20:53:06Z wikipedia>IceWelder 0 HTTPS wikitext text/x-wiki [https://www.mobygames.com/game{{Trim|{{{1|{{{id}}}}}}}} {{{2|{{{name|''{{PAGENAMEBASE}}''}}}}}}] at [[MobyGames]]<noinclude> {{documentation}} </noinclude> 2e12144d8e38e31c8db6a625b91644eae711192e Module:String 828 351 696 2020-08-02T15:49:42Z wikipedia>RexxS 0 separate annotations for str.match from those for str._match Scribunto text/plain --[[ This module is intended to provide access to basic string functions. Most of the functions provided here can be invoked with named parameters, unnamed parameters, or a mixture. If named parameters are used, Mediawiki will automatically remove any leading or trailing whitespace from the parameter. Depending on the intended use, it may be advantageous to either preserve or remove such whitespace. Global options ignore_errors: If set to 'true' or 1, any error condition will result in an empty string being returned rather than an error message. error_category: If an error occurs, specifies the name of a category to include with the error message. The default category is [Category:Errors reported by Module String]. no_category: If set to 'true' or 1, no category will be added if an error is generated. Unit tests for this module are available at Module:String/tests. ]] local str = {} --[[ len This function returns the length of the target string. Usage: {{#invoke:String|len|target_string|}} OR {{#invoke:String|len|s=target_string}} Parameters s: The string whose length to report If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from the target string. ]] function str.len( frame ) local new_args = str._getParameters( frame.args, {'s'} ) local s = new_args['s'] or '' return mw.ustring.len( s ) end --[[ sub This function returns a substring of the target string at specified indices. Usage: {{#invoke:String|sub|target_string|start_index|end_index}} OR {{#invoke:String|sub|s=target_string|i=start_index|j=end_index}} Parameters s: The string to return a subset of i: The fist index of the substring to return, defaults to 1. j: The last index of the string to return, defaults to the last character. The first character of the string is assigned an index of 1. If either i or j is a negative value, it is interpreted the same as selecting a character by counting from the end of the string. Hence, a value of -1 is the same as selecting the last character of the string. If the requested indices are out of range for the given string, an error is reported. ]] function str.sub( frame ) local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } ) local s = new_args['s'] or '' local i = tonumber( new_args['i'] ) or 1 local j = tonumber( new_args['j'] ) or -1 local len = mw.ustring.len( s ) -- Convert negatives for range checking if i < 0 then i = len + i + 1 end if j < 0 then j = len + j + 1 end if i > len or j > len or i < 1 or j < 1 then return str._error( 'String subset index out of range' ) end if j < i then return str._error( 'String subset indices out of order' ) end return mw.ustring.sub( s, i, j ) end --[[ This function implements that features of {{str sub old}} and is kept in order to maintain these older templates. ]] function str.sublength( frame ) local i = tonumber( frame.args.i ) or 0 local len = tonumber( frame.args.len ) return mw.ustring.sub( frame.args.s, i + 1, len and ( i + len ) ) end --[[ _match This function returns a substring from the source string that matches a specified pattern. It is exported for use in other modules Usage: strmatch = require("Module:String")._match sresult = strmatch( s, pattern, start, match, plain, nomatch ) Parameters s: The string to search pattern: The pattern or string to find within the string start: The index within the source string to start the search. The first character of the string has index 1. Defaults to 1. match: In some cases it may be possible to make multiple matches on a single string. This specifies which match to return, where the first match is match= 1. If a negative number is specified then a match is returned counting from the last match. Hence match = -1 is the same as requesting the last match. Defaults to 1. plain: A flag indicating that the pattern should be understood as plain text. Defaults to false. nomatch: If no match is found, output the "nomatch" value rather than an error. For information on constructing Lua patterns, a form of [regular expression], see: * http://www.lua.org/manual/5.1/manual.html#5.4.1 * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns ]] -- This sub-routine is exported for use in other modules function str._match( s, pattern, start, match_index, plain_flag, nomatch ) if s == '' then return str._error( 'Target string is empty' ) end if pattern == '' then return str._error( 'Pattern string is empty' ) end start = tonumber(start) or 1 if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then return str._error( 'Requested start is out of range' ) end if match_index == 0 then return str._error( 'Match index is out of range' ) end if plain_flag then pattern = str._escapePattern( pattern ) end local result if match_index == 1 then -- Find first match is simple case result = mw.ustring.match( s, pattern, start ) else if start > 1 then s = mw.ustring.sub( s, start ) end local iterator = mw.ustring.gmatch(s, pattern) if match_index > 0 then -- Forward search for w in iterator do match_index = match_index - 1 if match_index == 0 then result = w break end end else -- Reverse search local result_table = {} local count = 1 for w in iterator do result_table[count] = w count = count + 1 end result = result_table[ count + match_index ] end end if result == nil then if nomatch == nil then return str._error( 'Match not found' ) else return nomatch end else return result end end --[[ match This function returns a substring from the source string that matches a specified pattern. Usage: {{#invoke:String|match|source_string|pattern_string|start_index|match_number|plain_flag|nomatch_output}} OR {{#invoke:String|match|s=source_string|pattern=pattern_string|start=start_index |match=match_number|plain=plain_flag|nomatch=nomatch_output}} Parameters s: The string to search pattern: The pattern or string to find within the string start: The index within the source string to start the search. The first character of the string has index 1. Defaults to 1. match: In some cases it may be possible to make multiple matches on a single string. This specifies which match to return, where the first match is match= 1. If a negative number is specified then a match is returned counting from the last match. Hence match = -1 is the same as requesting the last match. Defaults to 1. plain: A flag indicating that the pattern should be understood as plain text. Defaults to false. nomatch: If no match is found, output the "nomatch" value rather than an error. If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from each string. In some circumstances this is desirable, in other cases one may want to preserve the whitespace. If the match_number or start_index are out of range for the string being queried, then this function generates an error. An error is also generated if no match is found. If one adds the parameter ignore_errors=true, then the error will be suppressed and an empty string will be returned on any failure. For information on constructing Lua patterns, a form of [regular expression], see: * http://www.lua.org/manual/5.1/manual.html#5.4.1 * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns ]] -- This is the entry point for #invoke:String|match function str.match( frame ) local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} ) local s = new_args['s'] or '' local start = tonumber( new_args['start'] ) or 1 local plain_flag = str._getBoolean( new_args['plain'] or false ) local pattern = new_args['pattern'] or '' local match_index = math.floor( tonumber(new_args['match']) or 1 ) local nomatch = new_args['nomatch'] return str._match( s, pattern, start, match_index, plain_flag, nomatch ) end --[[ pos This function returns a single character from the target string at position pos. Usage: {{#invoke:String|pos|target_string|index_value}} OR {{#invoke:String|pos|target=target_string|pos=index_value}} Parameters target: The string to search pos: The index for the character to return If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from the target string. In some circumstances this is desirable, in other cases one may want to preserve the whitespace. The first character has an index value of 1. If one requests a negative value, this function will select a character by counting backwards from the end of the string. In other words pos = -1 is the same as asking for the last character. A requested value of zero, or a value greater than the length of the string returns an error. ]] function str.pos( frame ) local new_args = str._getParameters( frame.args, {'target', 'pos'} ) local target_str = new_args['target'] or '' local pos = tonumber( new_args['pos'] ) or 0 if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then return str._error( 'String index out of range' ) end return mw.ustring.sub( target_str, pos, pos ) end --[[ str_find This function duplicates the behavior of {{str_find}}, including all of its quirks. This is provided in order to support existing templates, but is NOT RECOMMENDED for new code and templates. New code is recommended to use the "find" function instead. Returns the first index in "source" that is a match to "target". Indexing is 1-based, and the function returns -1 if the "target" string is not present in "source". Important Note: If the "target" string is empty / missing, this function returns a value of "1", which is generally unexpected behavior, and must be accounted for separatetly. ]] function str.str_find( frame ) local new_args = str._getParameters( frame.args, {'source', 'target'} ) local source_str = new_args['source'] or '' local target_str = new_args['target'] or '' if target_str == '' then return 1 end local start = mw.ustring.find( source_str, target_str, 1, true ) if start == nil then start = -1 end return start end --[[ find This function allows one to search for a target string or pattern within another string. Usage: {{#invoke:String|find|source_str|target_string|start_index|plain_flag}} OR {{#invoke:String|find|source=source_str|target=target_str|start=start_index|plain=plain_flag}} Parameters source: The string to search target: The string or pattern to find within source start: The index within the source string to start the search, defaults to 1 plain: Boolean flag indicating that target should be understood as plain text and not as a Lua style regular expression, defaults to true If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from the parameter. In some circumstances this is desirable, in other cases one may want to preserve the whitespace. This function returns the first index >= "start" where "target" can be found within "source". Indices are 1-based. If "target" is not found, then this function returns 0. If either "source" or "target" are missing / empty, this function also returns 0. This function should be safe for UTF-8 strings. ]] function str.find( frame ) local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } ) local source_str = new_args['source'] or '' local pattern = new_args['target'] or '' local start_pos = tonumber(new_args['start']) or 1 local plain = new_args['plain'] or true if source_str == '' or pattern == '' then return 0 end plain = str._getBoolean( plain ) local start = mw.ustring.find( source_str, pattern, start_pos, plain ) if start == nil then start = 0 end return start end --[[ replace This function allows one to replace a target string or pattern within another string. Usage: {{#invoke:String|replace|source_str|pattern_string|replace_string|replacement_count|plain_flag}} OR {{#invoke:String|replace|source=source_string|pattern=pattern_string|replace=replace_string| count=replacement_count|plain=plain_flag}} Parameters source: The string to search pattern: The string or pattern to find within source replace: The replacement text count: The number of occurences to replace, defaults to all. plain: Boolean flag indicating that pattern should be understood as plain text and not as a Lua style regular expression, defaults to true ]] function str.replace( frame ) local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } ) local source_str = new_args['source'] or '' local pattern = new_args['pattern'] or '' local replace = new_args['replace'] or '' local count = tonumber( new_args['count'] ) local plain = new_args['plain'] or true if source_str == '' or pattern == '' then return source_str end plain = str._getBoolean( plain ) if plain then pattern = str._escapePattern( pattern ) replace = mw.ustring.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences. end local result if count ~= nil then result = mw.ustring.gsub( source_str, pattern, replace, count ) else result = mw.ustring.gsub( source_str, pattern, replace ) end return result end --[[ simple function to pipe string.rep to templates. ]] function str.rep( frame ) local repetitions = tonumber( frame.args[2] ) if not repetitions then return str._error( 'function rep expects a number as second parameter, received "' .. ( frame.args[2] or '' ) .. '"' ) end return string.rep( frame.args[1] or '', repetitions ) end --[[ escapePattern This function escapes special characters from a Lua string pattern. See [1] for details on how patterns work. [1] https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns Usage: {{#invoke:String|escapePattern|pattern_string}} Parameters pattern_string: The pattern string to escape. ]] function str.escapePattern( frame ) local pattern_str = frame.args[1] if not pattern_str then return str._error( 'No pattern string specified' ) end local result = str._escapePattern( pattern_str ) return result end --[[ count This function counts the number of occurrences of one string in another. ]] function str.count(frame) local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'}) local source = args.source or '' local pattern = args.pattern or '' local plain = str._getBoolean(args.plain or true) if plain then pattern = str._escapePattern(pattern) end local _, count = mw.ustring.gsub(source, pattern, '') return count end --[[ endswith This function determines whether a string ends with another string. ]] function str.endswith(frame) local args = str._getParameters(frame.args, {'source', 'pattern'}) local source = args.source or '' local pattern = args.pattern or '' if pattern == '' then -- All strings end with the empty string. return "yes" end if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then return "yes" else return "" end end --[[ join Join all non empty arguments together; the first argument is the separator. Usage: {{#invoke:String|join|sep|one|two|three}} ]] function str.join(frame) local args = {} local sep for _, v in ipairs( frame.args ) do if sep then if v ~= '' then table.insert(args, v) end else sep = v end end return table.concat( args, sep or '' ) end --[[ Helper function that populates the argument list given that user may need to use a mix of named and unnamed parameters. This is relevant because named parameters are not identical to unnamed parameters due to string trimming, and when dealing with strings we sometimes want to either preserve or remove that whitespace depending on the application. ]] function str._getParameters( frame_args, arg_list ) local new_args = {} local index = 1 local value for _, arg in ipairs( arg_list ) do value = frame_args[arg] if value == nil then value = frame_args[index] index = index + 1 end new_args[arg] = value end return new_args end --[[ Helper function to handle error messages. ]] function str._error( error_str ) local frame = mw.getCurrentFrame() local error_category = frame.args.error_category or 'Errors reported by Module String' local ignore_errors = frame.args.ignore_errors or false local no_category = frame.args.no_category or false if str._getBoolean(ignore_errors) then return '' end local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>' if error_category ~= '' and not str._getBoolean( no_category ) then error_str = '[[Category:' .. error_category .. ']]' .. error_str end return error_str end --[[ Helper Function to interpret boolean strings ]] function str._getBoolean( boolean_str ) local boolean_value if type( boolean_str ) == 'string' then boolean_str = boolean_str:lower() if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0' or boolean_str == '' then boolean_value = false else boolean_value = true end elseif type( boolean_str ) == 'boolean' then boolean_value = boolean_str else error( 'No boolean value found' ) end return boolean_value end --[[ Helper function that escapes all pattern characters so that they will be treated as plain text. ]] function str._escapePattern( pattern_str ) return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ) end return str 6df794dd52434e0f6a372c9918f5a9dedd15f579 Module:Date 828 439 878 2020-08-03T02:55:18Z wikipedia>Johnuniq 0 update from sandbox: implement show=M (minutes) and show=s (seconds); better method to fill a partial date Scribunto text/plain -- Date functions for use by other modules. -- I18N and time zones are not supported. local MINUS = '−' -- Unicode U+2212 MINUS SIGN local floor = math.floor local Date, DateDiff, diffmt -- forward declarations local uniq = { 'unique identifier' } local function is_date(t) -- The system used to make a date read-only means there is no unique -- metatable that is conveniently accessible to check. return type(t) == 'table' and t._id == uniq end local function is_diff(t) return type(t) == 'table' and getmetatable(t) == diffmt end local function _list_join(list, sep) return table.concat(list, sep) end local function collection() -- Return a table to hold items. return { n = 0, add = function (self, item) self.n = self.n + 1 self[self.n] = item end, join = _list_join, } end local function strip_to_nil(text) -- If text is a string, return its trimmed content, or nil if empty. -- Otherwise return text (convenient when Date fields are provided from -- another module which may pass a string, a number, or another type). if type(text) == 'string' then text = text:match('(%S.-)%s*$') end return text end local function is_leap_year(year, calname) -- Return true if year is a leap year. if calname == 'Julian' then return year % 4 == 0 end return (year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0 end local function days_in_month(year, month, calname) -- Return number of days (1..31) in given month (1..12). if month == 2 and is_leap_year(year, calname) then return 29 end return ({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })[month] end local function h_m_s(time) -- Return hour, minute, second extracted from fraction of a day. time = floor(time * 24 * 3600 + 0.5) -- number of seconds local second = time % 60 time = floor(time / 60) return floor(time / 60), time % 60, second end local function hms(date) -- Return fraction of a day from date's time, where (0 <= fraction < 1) -- if the values are valid, but could be anything if outside range. return (date.hour + (date.minute + date.second / 60) / 60) / 24 end local function julian_date(date) -- Return jd, jdz from a Julian or Gregorian calendar date where -- jd = Julian date and its fractional part is zero at noon -- jdz = same, but assume time is 00:00:00 if no time given -- http://www.tondering.dk/claus/cal/julperiod.php#formula -- Testing shows this works for all dates from year -9999 to 9999! -- JDN 0 is the 24-hour period starting at noon UTC on Monday -- 1 January 4713 BC = (-4712, 1, 1) Julian calendar -- 24 November 4714 BC = (-4713, 11, 24) Gregorian calendar local offset local a = floor((14 - date.month)/12) local y = date.year + 4800 - a if date.calendar == 'Julian' then offset = floor(y/4) - 32083 else offset = floor(y/4) - floor(y/100) + floor(y/400) - 32045 end local m = date.month + 12*a - 3 local jd = date.day + floor((153*m + 2)/5) + 365*y + offset if date.hastime then jd = jd + hms(date) - 0.5 return jd, jd end return jd, jd - 0.5 end local function set_date_from_jd(date) -- Set the fields of table date from its Julian date field. -- Return true if date is valid. -- http://www.tondering.dk/claus/cal/julperiod.php#formula -- This handles the proleptic Julian and Gregorian calendars. -- Negative Julian dates are not defined but they work. local calname = date.calendar local low, high -- min/max limits for date ranges −9999-01-01 to 9999-12-31 if calname == 'Gregorian' then low, high = -1930999.5, 5373484.49999 elseif calname == 'Julian' then low, high = -1931076.5, 5373557.49999 else return end local jd = date.jd if not (type(jd) == 'number' and low <= jd and jd <= high) then return end local jdn = floor(jd) if date.hastime then local time = jd - jdn -- 0 <= time < 1 if time >= 0.5 then -- if at or after midnight of next day jdn = jdn + 1 time = time - 0.5 else time = time + 0.5 end date.hour, date.minute, date.second = h_m_s(time) else date.second = 0 date.minute = 0 date.hour = 0 end local b, c if calname == 'Julian' then b = 0 c = jdn + 32082 else -- Gregorian local a = jdn + 32044 b = floor((4*a + 3)/146097) c = a - floor(146097*b/4) end local d = floor((4*c + 3)/1461) local e = c - floor(1461*d/4) local m = floor((5*e + 2)/153) date.day = e - floor((153*m + 2)/5) + 1 date.month = m + 3 - 12*floor(m/10) date.year = 100*b + d - 4800 + floor(m/10) return true end local function fix_numbers(numbers, y, m, d, H, M, S, partial, hastime, calendar) -- Put the result of normalizing the given values in table numbers. -- The result will have valid m, d values if y is valid; caller checks y. -- The logic of PHP mktime is followed where m or d can be zero to mean -- the previous unit, and -1 is the one before that, etc. -- Positive values carry forward. local date if not (1 <= m and m <= 12) then date = Date(y, 1, 1) if not date then return end date = date + ((m - 1) .. 'm') y, m = date.year, date.month end local days_hms if not partial then if hastime and H and M and S then if not (0 <= H and H <= 23 and 0 <= M and M <= 59 and 0 <= S and S <= 59) then days_hms = hms({ hour = H, minute = M, second = S }) end end if days_hms or not (1 <= d and d <= days_in_month(y, m, calendar)) then date = date or Date(y, m, 1) if not date then return end date = date + (d - 1 + (days_hms or 0)) y, m, d = date.year, date.month, date.day if days_hms then H, M, S = date.hour, date.minute, date.second end end end numbers.year = y numbers.month = m numbers.day = d if days_hms then -- Don't set H unless it was valid because a valid H will set hastime. numbers.hour = H numbers.minute = M numbers.second = S end end local function set_date_from_numbers(date, numbers, options) -- Set the fields of table date from numeric values. -- Return true if date is valid. if type(numbers) ~= 'table' then return end local y = numbers.year or date.year local m = numbers.month or date.month local d = numbers.day or date.day local H = numbers.hour local M = numbers.minute or date.minute or 0 local S = numbers.second or date.second or 0 local need_fix if y and m and d then date.partial = nil if not (-9999 <= y and y <= 9999 and 1 <= m and m <= 12 and 1 <= d and d <= days_in_month(y, m, date.calendar)) then if not date.want_fix then return end need_fix = true end elseif y and date.partial then if d or not (-9999 <= y and y <= 9999) then return end if m and not (1 <= m and m <= 12) then if not date.want_fix then return end need_fix = true end else return end if date.partial then H = nil -- ignore any time M = nil S = nil else if H then -- It is not possible to set M or S without also setting H. date.hastime = true else H = 0 end if not (0 <= H and H <= 23 and 0 <= M and M <= 59 and 0 <= S and S <= 59) then if date.want_fix then need_fix = true else return end end end date.want_fix = nil if need_fix then fix_numbers(numbers, y, m, d, H, M, S, date.partial, date.hastime, date.calendar) return set_date_from_numbers(date, numbers, options) end date.year = y -- -9999 to 9999 ('n BC' → year = 1 - n) date.month = m -- 1 to 12 (may be nil if partial) date.day = d -- 1 to 31 (* = nil if partial) date.hour = H -- 0 to 59 (*) date.minute = M -- 0 to 59 (*) date.second = S -- 0 to 59 (*) if type(options) == 'table' then for _, k in ipairs({ 'am', 'era', 'format' }) do if options[k] then date.options[k] = options[k] end end end return true end local function make_option_table(options1, options2) -- If options1 is a string, return a table with its settings, or -- if it is a table, use its settings. -- Missing options are set from table options2 or defaults. -- If a default is used, a flag is set so caller knows the value was not intentionally set. -- Valid option settings are: -- am: 'am', 'a.m.', 'AM', 'A.M.' -- 'pm', 'p.m.', 'PM', 'P.M.' (each has same meaning as corresponding item above) -- era: 'BCMINUS', 'BCNEGATIVE', 'BC', 'B.C.', 'BCE', 'B.C.E.', 'AD', 'A.D.', 'CE', 'C.E.' -- Option am = 'am' does not mean the hour is AM; it means 'am' or 'pm' is used, depending on the hour, -- and am = 'pm' has the same meaning. -- Similarly, era = 'BC' means 'BC' is used if year <= 0. -- BCMINUS displays a MINUS if year < 0 and the display format does not include %{era}. -- BCNEGATIVE is similar but displays a hyphen. local result = { bydefault = {} } if type(options1) == 'table' then result.am = options1.am result.era = options1.era elseif type(options1) == 'string' then -- Example: 'am:AM era:BC' or 'am=AM era=BC'. for item in options1:gmatch('%S+') do local lhs, rhs = item:match('^(%w+)[:=](.+)$') if lhs then result[lhs] = rhs end end end options2 = type(options2) == 'table' and options2 or {} local defaults = { am = 'am', era = 'BC' } for k, v in pairs(defaults) do if not result[k] then if options2[k] then result[k] = options2[k] else result[k] = v result.bydefault[k] = true end end end return result end local ampm_options = { -- lhs = input text accepted as an am/pm option -- rhs = code used internally ['am'] = 'am', ['AM'] = 'AM', ['a.m.'] = 'a.m.', ['A.M.'] = 'A.M.', ['pm'] = 'am', -- same as am ['PM'] = 'AM', ['p.m.'] = 'a.m.', ['P.M.'] = 'A.M.', } local era_text = { -- Text for displaying an era with a positive year (after adjusting -- by replacing year with 1 - year if date.year <= 0). -- options.era = { year<=0 , year>0 } ['BCMINUS'] = { 'BC' , '' , isbc = true, sign = MINUS }, ['BCNEGATIVE'] = { 'BC' , '' , isbc = true, sign = '-' }, ['BC'] = { 'BC' , '' , isbc = true }, ['B.C.'] = { 'B.C.' , '' , isbc = true }, ['BCE'] = { 'BCE' , '' , isbc = true }, ['B.C.E.'] = { 'B.C.E.', '' , isbc = true }, ['AD'] = { 'BC' , 'AD' }, ['A.D.'] = { 'B.C.' , 'A.D.' }, ['CE'] = { 'BCE' , 'CE' }, ['C.E.'] = { 'B.C.E.', 'C.E.' }, } local function get_era_for_year(era, year) return (era_text[era] or era_text['BC'])[year > 0 and 2 or 1] or '' end local function strftime(date, format, options) -- Return date formatted as a string using codes similar to those -- in the C strftime library function. local sformat = string.format local shortcuts = { ['%c'] = '%-I:%M %p %-d %B %-Y %{era}', -- date and time: 2:30 pm 1 April 2016 ['%x'] = '%-d %B %-Y %{era}', -- date: 1 April 2016 ['%X'] = '%-I:%M %p', -- time: 2:30 pm } if shortcuts[format] then format = shortcuts[format] end local codes = { a = { field = 'dayabbr' }, A = { field = 'dayname' }, b = { field = 'monthabbr' }, B = { field = 'monthname' }, u = { fmt = '%d' , field = 'dowiso' }, w = { fmt = '%d' , field = 'dow' }, d = { fmt = '%02d', fmt2 = '%d', field = 'day' }, m = { fmt = '%02d', fmt2 = '%d', field = 'month' }, Y = { fmt = '%04d', fmt2 = '%d', field = 'year' }, H = { fmt = '%02d', fmt2 = '%d', field = 'hour' }, M = { fmt = '%02d', fmt2 = '%d', field = 'minute' }, S = { fmt = '%02d', fmt2 = '%d', field = 'second' }, j = { fmt = '%03d', fmt2 = '%d', field = 'dayofyear' }, I = { fmt = '%02d', fmt2 = '%d', field = 'hour', special = 'hour12' }, p = { field = 'hour', special = 'am' }, } options = make_option_table(options, date.options) local amopt = options.am local eraopt = options.era local function replace_code(spaces, modifier, id) local code = codes[id] if code then local fmt = code.fmt if modifier == '-' and code.fmt2 then fmt = code.fmt2 end local value = date[code.field] if not value then return nil -- an undefined field in a partial date end local special = code.special if special then if special == 'hour12' then value = value % 12 value = value == 0 and 12 or value elseif special == 'am' then local ap = ({ ['a.m.'] = { 'a.m.', 'p.m.' }, ['AM'] = { 'AM', 'PM' }, ['A.M.'] = { 'A.M.', 'P.M.' }, })[ampm_options[amopt]] or { 'am', 'pm' } return (spaces == '' and '' or '&nbsp;') .. (value < 12 and ap[1] or ap[2]) end end if code.field == 'year' then local sign = (era_text[eraopt] or {}).sign if not sign or format:find('%{era}', 1, true) then sign = '' if value <= 0 then value = 1 - value end else if value >= 0 then sign = '' else value = -value end end return spaces .. sign .. sformat(fmt, value) end return spaces .. (fmt and sformat(fmt, value) or value) end end local function replace_property(spaces, id) if id == 'era' then -- Special case so can use local era option. local result = get_era_for_year(eraopt, date.year) if result == '' then return '' end return (spaces == '' and '' or '&nbsp;') .. result end local result = date[id] if type(result) == 'string' then return spaces .. result end if type(result) == 'number' then return spaces .. tostring(result) end if type(result) == 'boolean' then return spaces .. (result and '1' or '0') end -- This occurs if id is an undefined field in a partial date, or is the name of a function. return nil end local PERCENT = '\127PERCENT\127' return (format :gsub('%%%%', PERCENT) :gsub('(%s*)%%{(%w+)}', replace_property) :gsub('(%s*)%%(%-?)(%a)', replace_code) :gsub(PERCENT, '%%') ) end local function _date_text(date, fmt, options) -- Return a formatted string representing the given date. if not is_date(date) then error('date:text: need a date (use "date:text()" with a colon)', 2) end if type(fmt) == 'string' and fmt:match('%S') then if fmt:find('%', 1, true) then return strftime(date, fmt, options) end elseif date.partial then fmt = date.month and 'my' or 'y' else fmt = 'dmy' if date.hastime then fmt = (date.second > 0 and 'hms ' or 'hm ') .. fmt end end local function bad_format() -- For consistency with other format processing, return given format -- (or cleaned format if original was not a string) if invalid. return mw.text.nowiki(fmt) end if date.partial then -- Ignore days in standard formats like 'ymd'. if fmt == 'ym' or fmt == 'ymd' then fmt = date.month and '%Y-%m %{era}' or '%Y %{era}' elseif fmt == 'my' or fmt == 'dmy' or fmt == 'mdy' then fmt = date.month and '%B %-Y %{era}' or '%-Y %{era}' elseif fmt == 'y' then fmt = date.month and '%-Y %{era}' or '%-Y %{era}' else return bad_format() end return strftime(date, fmt, options) end local function hm_fmt() local plain = make_option_table(options, date.options).bydefault.am return plain and '%H:%M' or '%-I:%M %p' end local need_time = date.hastime local t = collection() for item in fmt:gmatch('%S+') do local f if item == 'hm' then f = hm_fmt() need_time = false elseif item == 'hms' then f = '%H:%M:%S' need_time = false elseif item == 'ymd' then f = '%Y-%m-%d %{era}' elseif item == 'mdy' then f = '%B %-d, %-Y %{era}' elseif item == 'dmy' then f = '%-d %B %-Y %{era}' else return bad_format() end t:add(f) end fmt = t:join(' ') if need_time then fmt = hm_fmt() .. ' ' .. fmt end return strftime(date, fmt, options) end local day_info = { -- 0=Sun to 6=Sat [0] = { 'Sun', 'Sunday' }, { 'Mon', 'Monday' }, { 'Tue', 'Tuesday' }, { 'Wed', 'Wednesday' }, { 'Thu', 'Thursday' }, { 'Fri', 'Friday' }, { 'Sat', 'Saturday' }, } local month_info = { -- 1=Jan to 12=Dec { 'Jan', 'January' }, { 'Feb', 'February' }, { 'Mar', 'March' }, { 'Apr', 'April' }, { 'May', 'May' }, { 'Jun', 'June' }, { 'Jul', 'July' }, { 'Aug', 'August' }, { 'Sep', 'September' }, { 'Oct', 'October' }, { 'Nov', 'November' }, { 'Dec', 'December' }, } local function name_to_number(text, translate) if type(text) == 'string' then return translate[text:lower()] end end local function day_number(text) return name_to_number(text, { sun = 0, sunday = 0, mon = 1, monday = 1, tue = 2, tuesday = 2, wed = 3, wednesday = 3, thu = 4, thursday = 4, fri = 5, friday = 5, sat = 6, saturday = 6, }) end local function month_number(text) return name_to_number(text, { jan = 1, january = 1, feb = 2, february = 2, mar = 3, march = 3, apr = 4, april = 4, may = 5, jun = 6, june = 6, jul = 7, july = 7, aug = 8, august = 8, sep = 9, september = 9, sept = 9, oct = 10, october = 10, nov = 11, november = 11, dec = 12, december = 12, }) end local function _list_text(list, fmt) -- Return a list of formatted strings from a list of dates. if not type(list) == 'table' then error('date:list:text: need "list:text()" with a colon', 2) end local result = { join = _list_join } for i, date in ipairs(list) do result[i] = date:text(fmt) end return result end local function _date_list(date, spec) -- Return a possibly empty numbered table of dates meeting the specification. -- Dates in the list are in ascending order (oldest date first). -- The spec should be a string of form "<count> <day> <op>" -- where each item is optional and -- count = number of items wanted in list -- day = abbreviation or name such as Mon or Monday -- op = >, >=, <, <= (default is > meaning after date) -- If no count is given, the list is for the specified days in date's month. -- The default day is date's day. -- The spec can also be a positive or negative number: -- -5 is equivalent to '5 <' -- 5 is equivalent to '5' which is '5 >' if not is_date(date) then error('date:list: need a date (use "date:list()" with a colon)', 2) end local list = { text = _list_text } if date.partial then return list end local count, offset, operation local ops = { ['>='] = { before = false, include = true }, ['>'] = { before = false, include = false }, ['<='] = { before = true , include = true }, ['<'] = { before = true , include = false }, } if spec then if type(spec) == 'number' then count = floor(spec + 0.5) if count < 0 then count = -count operation = ops['<'] end elseif type(spec) == 'string' then local num, day, op = spec:match('^%s*(%d*)%s*(%a*)%s*([<>=]*)%s*$') if not num then return list end if num ~= '' then count = tonumber(num) end if day ~= '' then local dow = day_number(day:gsub('[sS]$', '')) -- accept plural days if not dow then return list end offset = dow - date.dow end operation = ops[op] else return list end end offset = offset or 0 operation = operation or ops['>'] local datefrom, dayfirst, daylast if operation.before then if offset > 0 or (offset == 0 and not operation.include) then offset = offset - 7 end if count then if count > 1 then offset = offset - 7*(count - 1) end datefrom = date + offset else daylast = date.day + offset dayfirst = daylast % 7 if dayfirst == 0 then dayfirst = 7 end end else if offset < 0 or (offset == 0 and not operation.include) then offset = offset + 7 end if count then datefrom = date + offset else dayfirst = date.day + offset daylast = date.monthdays end end if not count then if daylast < dayfirst then return list end count = floor((daylast - dayfirst)/7) + 1 datefrom = Date(date, {day = dayfirst}) end for i = 1, count do if not datefrom then break end -- exceeds date limits list[i] = datefrom datefrom = datefrom + 7 end return list end -- A table to get the current date/time (UTC), but only if needed. local current = setmetatable({}, { __index = function (self, key) local d = os.date('!*t') self.year = d.year self.month = d.month self.day = d.day self.hour = d.hour self.minute = d.min self.second = d.sec return rawget(self, key) end }) local function extract_date(newdate, text) -- Parse the date/time in text and return n, o where -- n = table of numbers with date/time fields -- o = table of options for AM/PM or AD/BC or format, if any -- or return nothing if date is known to be invalid. -- Caller determines if the values in n are valid. -- A year must be positive ('1' to '9999'); use 'BC' for BC. -- In a y-m-d string, the year must be four digits to avoid ambiguity -- ('0001' to '9999'). The only way to enter year <= 0 is by specifying -- the date as three numeric parameters like ymd Date(-1, 1, 1). -- Dates of form d/m/y, m/d/y, y/m/d are rejected as potentially ambiguous. local date, options = {}, {} if text:sub(-1) == 'Z' then -- Extract date/time from a Wikidata timestamp. -- The year can be 1 to 16 digits but this module handles 1 to 4 digits only. -- Examples: '+2016-06-21T14:30:00Z', '-0000000180-00-00T00:00:00Z'. local sign, y, m, d, H, M, S = text:match('^([+%-])(%d+)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)Z$') if sign then y = tonumber(y) if sign == '-' and y > 0 then y = -y end if y <= 0 then options.era = 'BCE' end date.year = y m = tonumber(m) d = tonumber(d) H = tonumber(H) M = tonumber(M) S = tonumber(S) if m == 0 then newdate.partial = true return date, options end date.month = m if d == 0 then newdate.partial = true return date, options end date.day = d if H > 0 or M > 0 or S > 0 then date.hour = H date.minute = M date.second = S end return date, options end return end local function extract_ymd(item) -- Called when no day or month has been set. local y, m, d = item:match('^(%d%d%d%d)%-(%w+)%-(%d%d?)$') if y then if date.year then return end if m:match('^%d%d?$') then m = tonumber(m) else m = month_number(m) end if m then date.year = tonumber(y) date.month = m date.day = tonumber(d) return true end end end local function extract_day_or_year(item) -- Called when a day would be valid, or -- when a year would be valid if no year has been set and partial is set. local number, suffix = item:match('^(%d%d?%d?%d?)(.*)$') if number then local n = tonumber(number) if #number <= 2 and n <= 31 then suffix = suffix:lower() if suffix == '' or suffix == 'st' or suffix == 'nd' or suffix == 'rd' or suffix == 'th' then date.day = n return true end elseif suffix == '' and newdate.partial and not date.year then date.year = n return true end end end local function extract_month(item) -- A month must be given as a name or abbreviation; a number could be ambiguous. local m = month_number(item) if m then date.month = m return true end end local function extract_time(item) local h, m, s = item:match('^(%d%d?):(%d%d)(:?%d*)$') if date.hour or not h then return end if s ~= '' then s = s:match('^:(%d%d)$') if not s then return end end date.hour = tonumber(h) date.minute = tonumber(m) date.second = tonumber(s) -- nil if empty string return true end local item_count = 0 local index_time local function set_ampm(item) local H = date.hour if H and not options.am and index_time + 1 == item_count then options.am = ampm_options[item] -- caller checked this is not nil if item:match('^[Aa]') then if not (1 <= H and H <= 12) then return end if H == 12 then date.hour = 0 end else if not (1 <= H and H <= 23) then return end if H <= 11 then date.hour = H + 12 end end return true end end for item in text:gsub(',', ' '):gsub('&nbsp;', ' '):gmatch('%S+') do item_count = item_count + 1 if era_text[item] then -- Era is accepted in peculiar places. if options.era then return end options.era = item elseif ampm_options[item] then if not set_ampm(item) then return end elseif item:find(':', 1, true) then if not extract_time(item) then return end index_time = item_count elseif date.day and date.month then if date.year then return -- should be nothing more so item is invalid end if not item:match('^(%d%d?%d?%d?)$') then return end date.year = tonumber(item) elseif date.day then if not extract_month(item) then return end elseif date.month then if not extract_day_or_year(item) then return end elseif extract_month(item) then options.format = 'mdy' elseif extract_ymd(item) then options.format = 'ymd' elseif extract_day_or_year(item) then if date.day then options.format = 'dmy' end else return end end if not date.year or date.year == 0 then return end local era = era_text[options.era] if era and era.isbc then date.year = 1 - date.year end return date, options end local function autofill(date1, date2) -- Fill any missing month or day in each date using the -- corresponding component from the other date, if present, -- or with 1 if both dates are missing the month or day. -- This gives a good result for calculating the difference -- between two partial dates when no range is wanted. -- Return filled date1, date2 (two full dates). local function filled(a, b) -- Return date a filled, if necessary, with month and/or day from date b. -- The filled day is truncated to fit the number of days in the month. local fillmonth, fillday if not a.month then fillmonth = b.month or 1 end if not a.day then fillday = b.day or 1 end if fillmonth or fillday then -- need to create a new date a = Date(a, { month = fillmonth, day = math.min(fillday or a.day, days_in_month(a.year, fillmonth or a.month, a.calendar)) }) end return a end return filled(date1, date2), filled(date2, date1) end local function date_add_sub(lhs, rhs, is_sub) -- Return a new date from calculating (lhs + rhs) or (lhs - rhs), -- or return nothing if invalid. -- The result is nil if the calculated date exceeds allowable limits. -- Caller ensures that lhs is a date; its properties are copied for the new date. if lhs.partial then -- Adding to a partial is not supported. -- Can subtract a date or partial from a partial, but this is not called for that. return end local function is_prefix(text, word, minlen) local n = #text return (minlen or 1) <= n and n <= #word and text == word:sub(1, n) end local function do_days(n) local forcetime, jd if floor(n) == n then jd = lhs.jd else forcetime = not lhs.hastime jd = lhs.jdz end jd = jd + (is_sub and -n or n) if forcetime then jd = tostring(jd) if not jd:find('.', 1, true) then jd = jd .. '.0' end end return Date(lhs, 'juliandate', jd) end if type(rhs) == 'number' then -- Add/subtract days, including fractional days. return do_days(rhs) end if type(rhs) == 'string' then -- rhs is a single component like '26m' or '26 months' (with optional sign). -- Fractions like '3.25d' are accepted for the units which are handled as days. local sign, numstr, id = rhs:match('^%s*([+-]?)([%d%.]+)%s*(%a+)$') if sign then if sign == '-' then is_sub = not (is_sub and true or false) end local y, m, days local num = tonumber(numstr) if not num then return end id = id:lower() if is_prefix(id, 'years') then y = num m = 0 elseif is_prefix(id, 'months') then y = floor(num / 12) m = num % 12 elseif is_prefix(id, 'weeks') then days = num * 7 elseif is_prefix(id, 'days') then days = num elseif is_prefix(id, 'hours') then days = num / 24 elseif is_prefix(id, 'minutes', 3) then days = num / (24 * 60) elseif is_prefix(id, 'seconds') then days = num / (24 * 3600) else return end if days then return do_days(days) end if numstr:find('.', 1, true) then return end if is_sub then y = -y m = -m end assert(-11 <= m and m <= 11) y = lhs.year + y m = lhs.month + m if m > 12 then y = y + 1 m = m - 12 elseif m < 1 then y = y - 1 m = m + 12 end local d = math.min(lhs.day, days_in_month(y, m, lhs.calendar)) return Date(lhs, y, m, d) end end if is_diff(rhs) then local days = rhs.age_days if (is_sub or false) ~= (rhs.isnegative or false) then days = -days end return lhs + days end end local full_date_only = { dayabbr = true, dayname = true, dow = true, dayofweek = true, dowiso = true, dayofweekiso = true, dayofyear = true, gsd = true, juliandate = true, jd = true, jdz = true, jdnoon = true, } -- Metatable for a date's calculated fields. local datemt = { __index = function (self, key) if rawget(self, 'partial') then if full_date_only[key] then return end if key == 'monthabbr' or key == 'monthdays' or key == 'monthname' then if not self.month then return end end end local value if key == 'dayabbr' then value = day_info[self.dow][1] elseif key == 'dayname' then value = day_info[self.dow][2] elseif key == 'dow' then value = (self.jdnoon + 1) % 7 -- day-of-week 0=Sun to 6=Sat elseif key == 'dayofweek' then value = self.dow elseif key == 'dowiso' then value = (self.jdnoon % 7) + 1 -- ISO day-of-week 1=Mon to 7=Sun elseif key == 'dayofweekiso' then value = self.dowiso elseif key == 'dayofyear' then local first = Date(self.year, 1, 1, self.calendar).jdnoon value = self.jdnoon - first + 1 -- day-of-year 1 to 366 elseif key == 'era' then -- Era text (never a negative sign) from year and options. value = get_era_for_year(self.options.era, self.year) elseif key == 'format' then value = self.options.format or 'dmy' elseif key == 'gsd' then -- GSD = 1 from 00:00:00 to 23:59:59 on 1 January 1 AD Gregorian calendar, -- which is from jd 1721425.5 to 1721426.49999. value = floor(self.jd - 1721424.5) elseif key == 'juliandate' or key == 'jd' or key == 'jdz' then local jd, jdz = julian_date(self) rawset(self, 'juliandate', jd) rawset(self, 'jd', jd) rawset(self, 'jdz', jdz) return key == 'jdz' and jdz or jd elseif key == 'jdnoon' then -- Julian date at noon (an integer) on the calendar day when jd occurs. value = floor(self.jd + 0.5) elseif key == 'isleapyear' then value = is_leap_year(self.year, self.calendar) elseif key == 'monthabbr' then value = month_info[self.month][1] elseif key == 'monthdays' then value = days_in_month(self.year, self.month, self.calendar) elseif key == 'monthname' then value = month_info[self.month][2] end if value ~= nil then rawset(self, key, value) return value end end, } -- Date operators. local function mt_date_add(lhs, rhs) if not is_date(lhs) then lhs, rhs = rhs, lhs -- put date on left (it must be a date for this to have been called) end return date_add_sub(lhs, rhs) end local function mt_date_sub(lhs, rhs) if is_date(lhs) then if is_date(rhs) then return DateDiff(lhs, rhs) end return date_add_sub(lhs, rhs, true) end end local function mt_date_concat(lhs, rhs) return tostring(lhs) .. tostring(rhs) end local function mt_date_tostring(self) return self:text() end local function mt_date_eq(lhs, rhs) -- Return true if dates identify same date/time where, for example, -- Date(-4712, 1, 1, 'Julian') == Date(-4713, 11, 24, 'Gregorian') is true. -- This is called only if lhs and rhs have the same type and the same metamethod. if lhs.partial or rhs.partial then -- One date is partial; the other is a partial or a full date. -- The months may both be nil, but must be the same. return lhs.year == rhs.year and lhs.month == rhs.month and lhs.calendar == rhs.calendar end return lhs.jdz == rhs.jdz end local function mt_date_lt(lhs, rhs) -- Return true if lhs < rhs, for example, -- Date('1 Jan 2016') < Date('06:00 1 Jan 2016') is true. -- This is called only if lhs and rhs have the same type and the same metamethod. if lhs.partial or rhs.partial then -- One date is partial; the other is a partial or a full date. if lhs.calendar ~= rhs.calendar then return lhs.calendar == 'Julian' end if lhs.partial then lhs = lhs.partial.first end if rhs.partial then rhs = rhs.partial.first end end return lhs.jdz < rhs.jdz end --[[ Examples of syntax to construct a date: Date(y, m, d, 'julian') default calendar is 'gregorian' Date(y, m, d, H, M, S, 'julian') Date('juliandate', jd, 'julian') if jd contains "." text output includes H:M:S Date('currentdate') Date('currentdatetime') Date('1 April 1995', 'julian') parse date from text Date('1 April 1995 AD', 'julian') using an era sets a flag to do the same for output Date('04:30:59 1 April 1995', 'julian') Date(date) copy of an existing date Date(date, t) same, updated with y,m,d,H,M,S fields from table t Date(t) date with y,m,d,H,M,S fields from table t ]] function Date(...) -- for forward declaration above -- Return a table holding a date assuming a uniform calendar always applies -- (proleptic Gregorian calendar or proleptic Julian calendar), or -- return nothing if date is invalid. -- A partial date has a valid year, however its month may be nil, and -- its day and time fields are nil. -- Field partial is set to false (if a full date) or a table (if a partial date). local calendars = { julian = 'Julian', gregorian = 'Gregorian' } local newdate = { _id = uniq, calendar = 'Gregorian', -- default is Gregorian calendar hastime = false, -- true if input sets a time hour = 0, -- always set hour/minute/second so don't have to handle nil minute = 0, second = 0, options = {}, list = _date_list, subtract = function (self, rhs, options) return DateDiff(self, rhs, options) end, text = _date_text, } local argtype, datetext, is_copy, jd_number, tnums local numindex = 0 local numfields = { 'year', 'month', 'day', 'hour', 'minute', 'second' } local numbers = {} for _, v in ipairs({...}) do v = strip_to_nil(v) local vlower = type(v) == 'string' and v:lower() or nil if v == nil then -- Ignore empty arguments after stripping so modules can directly pass template parameters. elseif calendars[vlower] then newdate.calendar = calendars[vlower] elseif vlower == 'partial' then newdate.partial = true elseif vlower == 'fix' then newdate.want_fix = true elseif is_date(v) then -- Copy existing date (items can be overridden by other arguments). if is_copy or tnums then return end is_copy = true newdate.calendar = v.calendar newdate.partial = v.partial newdate.hastime = v.hastime newdate.options = v.options newdate.year = v.year newdate.month = v.month newdate.day = v.day newdate.hour = v.hour newdate.minute = v.minute newdate.second = v.second elseif type(v) == 'table' then if tnums then return end tnums = {} local tfields = { year=1, month=1, day=1, hour=2, minute=2, second=2 } for tk, tv in pairs(v) do if tfields[tk] then tnums[tk] = tonumber(tv) end if tfields[tk] == 2 then newdate.hastime = true end end else local num = tonumber(v) if not num and argtype == 'setdate' and numindex == 1 then num = month_number(v) end if num then if not argtype then argtype = 'setdate' end if argtype == 'setdate' and numindex < 6 then numindex = numindex + 1 numbers[numfields[numindex]] = num elseif argtype == 'juliandate' and not jd_number then jd_number = num if type(v) == 'string' then if v:find('.', 1, true) then newdate.hastime = true end elseif num ~= floor(num) then -- The given value was a number. The time will be used -- if the fractional part is nonzero. newdate.hastime = true end else return end elseif argtype then return elseif type(v) == 'string' then if v == 'currentdate' or v == 'currentdatetime' or v == 'juliandate' then argtype = v else argtype = 'datetext' datetext = v end else return end end end if argtype == 'datetext' then if tnums or not set_date_from_numbers(newdate, extract_date(newdate, datetext)) then return end elseif argtype == 'juliandate' then newdate.partial = nil newdate.jd = jd_number if not set_date_from_jd(newdate) then return end elseif argtype == 'currentdate' or argtype == 'currentdatetime' then newdate.partial = nil newdate.year = current.year newdate.month = current.month newdate.day = current.day if argtype == 'currentdatetime' then newdate.hour = current.hour newdate.minute = current.minute newdate.second = current.second newdate.hastime = true end newdate.calendar = 'Gregorian' -- ignore any given calendar name elseif argtype == 'setdate' then if tnums or not set_date_from_numbers(newdate, numbers) then return end elseif not (is_copy or tnums) then return end if tnums then newdate.jd = nil -- force recalculation in case jd was set before changes from tnums if not set_date_from_numbers(newdate, tnums) then return end end if newdate.partial then local year = newdate.year local month = newdate.month local first = Date(year, month or 1, 1, newdate.calendar) month = month or 12 local last = Date(year, month, days_in_month(year, month), newdate.calendar) newdate.partial = { first = first, last = last } else newdate.partial = false -- avoid index lookup end setmetatable(newdate, datemt) local readonly = {} local mt = { __index = newdate, __newindex = function(t, k, v) error('date.' .. tostring(k) .. ' is read-only', 2) end, __add = mt_date_add, __sub = mt_date_sub, __concat = mt_date_concat, __tostring = mt_date_tostring, __eq = mt_date_eq, __lt = mt_date_lt, } return setmetatable(readonly, mt) end local function _diff_age(diff, code, options) -- Return a tuple of integer values from diff as specified by code, except that -- each integer may be a list of two integers for a diff with a partial date, or -- return nil if the code is not supported. -- If want round, the least significant unit is rounded to nearest whole unit. -- For a duration, an extra day is added. local wantround, wantduration, wantrange if type(options) == 'table' then wantround = options.round wantduration = options.duration wantrange = options.range else wantround = options end if not is_diff(diff) then local f = wantduration and 'duration' or 'age' error(f .. ': need a date difference (use "diff:' .. f .. '()" with a colon)', 2) end if diff.partial then -- Ignore wantround, wantduration. local function choose(v) if type(v) == 'table' then if not wantrange or v[1] == v[2] then -- Example: Date('partial', 2005) - Date('partial', 2001) gives -- diff.years = { 3, 4 } to show the range of possible results. -- If do not want a range, choose the second value as more expected. return v[2] end end return v end if code == 'ym' or code == 'ymd' then if not wantrange and diff.iszero then -- This avoids an unexpected result such as -- Date('partial', 2001) - Date('partial', 2001) -- giving diff = { years = 0, months = { 0, 11 } } -- which would be reported as 0 years and 11 months. return 0, 0 end return choose(diff.partial.years), choose(diff.partial.months) end if code == 'y' then return choose(diff.partial.years) end if code == 'm' or code == 'w' or code == 'd' then return choose({ diff.partial.mindiff:age(code), diff.partial.maxdiff:age(code) }) end return nil end local extra_days = wantduration and 1 or 0 if code == 'wd' or code == 'w' or code == 'd' then local offset = wantround and 0.5 or 0 local days = diff.age_days + extra_days if code == 'wd' or code == 'd' then days = floor(days + offset) if code == 'd' then return days end return floor(days/7), days % 7 end return floor(days/7 + offset) end local H, M, S = diff.hours, diff.minutes, diff.seconds if code == 'dh' or code == 'dhm' or code == 'dhms' or code == 'h' or code == 'hm' or code == 'hms' or code == 'M' or code == 's' then local days = floor(diff.age_days + extra_days) local inc_hour if wantround then if code == 'dh' or code == 'h' then if M >= 30 then inc_hour = true end elseif code == 'dhm' or code == 'hm' then if S >= 30 then M = M + 1 if M >= 60 then M = 0 inc_hour = true end end elseif code == 'M' then if S >= 30 then M = M + 1 end else -- Nothing needed because S is an integer. end if inc_hour then H = H + 1 if H >= 24 then H = 0 days = days + 1 end end end if code == 'dh' or code == 'dhm' or code == 'dhms' then if code == 'dh' then return days, H elseif code == 'dhm' then return days, H, M else return days, H, M, S end end local hours = days * 24 + H if code == 'h' then return hours elseif code == 'hm' then return hours, M elseif code == 'M' or code == 's' then M = hours * 60 + M if code == 'M' then return M end return M * 60 + S end return hours, M, S end if wantround then local inc_hour if code == 'ymdh' or code == 'ymwdh' then if M >= 30 then inc_hour = true end elseif code == 'ymdhm' or code == 'ymwdhm' then if S >= 30 then M = M + 1 if M >= 60 then M = 0 inc_hour = true end end elseif code == 'ymd' or code == 'ymwd' or code == 'yd' or code == 'md' then if H >= 12 then extra_days = extra_days + 1 end end if inc_hour then H = H + 1 if H >= 24 then H = 0 extra_days = extra_days + 1 end end end local y, m, d = diff.years, diff.months, diff.days if extra_days > 0 then d = d + extra_days if d > 28 or code == 'yd' then -- Recalculate in case have passed a month. diff = diff.date1 + extra_days - diff.date2 y, m, d = diff.years, diff.months, diff.days end end if code == 'ymd' then return y, m, d elseif code == 'yd' then if y > 0 then -- It is known that diff.date1 > diff.date2. diff = diff.date1 - (diff.date2 + (y .. 'y')) end return y, floor(diff.age_days) elseif code == 'md' then return y * 12 + m, d elseif code == 'ym' or code == 'm' then if wantround then if d >= 16 then m = m + 1 if m >= 12 then m = 0 y = y + 1 end end end if code == 'ym' then return y, m end return y * 12 + m elseif code == 'ymw' then local weeks = floor(d/7) if wantround then local days = d % 7 if days > 3 or (days == 3 and H >= 12) then weeks = weeks + 1 end end return y, m, weeks elseif code == 'ymwd' then return y, m, floor(d/7), d % 7 elseif code == 'ymdh' then return y, m, d, H elseif code == 'ymwdh' then return y, m, floor(d/7), d % 7, H elseif code == 'ymdhm' then return y, m, d, H, M elseif code == 'ymwdhm' then return y, m, floor(d/7), d % 7, H, M end if code == 'y' then if wantround and m >= 6 then y = y + 1 end return y end return nil end local function _diff_duration(diff, code, options) if type(options) ~= 'table' then options = { round = options } end options.duration = true return _diff_age(diff, code, options) end -- Metatable for some operations on date differences. diffmt = { -- for forward declaration above __concat = function (lhs, rhs) return tostring(lhs) .. tostring(rhs) end, __tostring = function (self) return tostring(self.age_days) end, __index = function (self, key) local value if key == 'age_days' then if rawget(self, 'partial') then local function jdz(date) return (date.partial and date.partial.first or date).jdz end value = jdz(self.date1) - jdz(self.date2) else value = self.date1.jdz - self.date2.jdz end end if value ~= nil then rawset(self, key, value) return value end end, } function DateDiff(date1, date2, options) -- for forward declaration above -- Return a table with the difference between two dates (date1 - date2). -- The difference is negative if date1 is older than date2. -- Return nothing if invalid. -- If d = date1 - date2 then -- date1 = date2 + d -- If date1 >= date2 and the dates have no H:M:S time specified then -- date1 = date2 + (d.years..'y') + (d.months..'m') + d.days -- where the larger time units are added first. -- The result of Date(2015,1,x) + '1m' is Date(2015,2,28) for -- x = 28, 29, 30, 31. That means, for example, -- d = Date(2015,3,3) - Date(2015,1,31) -- gives d.years, d.months, d.days = 0, 1, 3 (excluding date1). if not (is_date(date1) and is_date(date2) and date1.calendar == date2.calendar) then return end local wantfill if type(options) == 'table' then wantfill = options.fill end local isnegative = false local iszero = false if date1 < date2 then isnegative = true date1, date2 = date2, date1 elseif date1 == date2 then iszero = true end -- It is known that date1 >= date2 (period is from date2 to date1). if date1.partial or date2.partial then -- Two partial dates might have timelines: ---------------------A=================B--- date1 is from A to B inclusive --------C=======D-------------------------- date2 is from C to D inclusive -- date1 > date2 iff A > C (date1.partial.first > date2.partial.first) -- The periods can overlap ('April 2001' - '2001'): -------------A===B------------------------- A=2001-04-01 B=2001-04-30 --------C=====================D------------ C=2001-01-01 D=2001-12-31 if wantfill then date1, date2 = autofill(date1, date2) else local function zdiff(date1, date2) local diff = date1 - date2 if diff.isnegative then return date1 - date1 -- a valid diff in case we call its methods end return diff end local function getdate(date, which) return date.partial and date.partial[which] or date end local maxdiff = zdiff(getdate(date1, 'last'), getdate(date2, 'first')) local mindiff = zdiff(getdate(date1, 'first'), getdate(date2, 'last')) local years, months if maxdiff.years == mindiff.years then years = maxdiff.years if maxdiff.months == mindiff.months then months = maxdiff.months else months = { mindiff.months, maxdiff.months } end else years = { mindiff.years, maxdiff.years } end return setmetatable({ date1 = date1, date2 = date2, partial = { years = years, months = months, maxdiff = maxdiff, mindiff = mindiff, }, isnegative = isnegative, iszero = iszero, age = _diff_age, duration = _diff_duration, }, diffmt) end end local y1, m1 = date1.year, date1.month local y2, m2 = date2.year, date2.month local years = y1 - y2 local months = m1 - m2 local d1 = date1.day + hms(date1) local d2 = date2.day + hms(date2) local days, time if d1 >= d2 then days = d1 - d2 else months = months - 1 -- Get days in previous month (before the "to" date) given December has 31 days. local dpm = m1 > 1 and days_in_month(y1, m1 - 1, date1.calendar) or 31 if d2 >= dpm then days = d1 - hms(date2) else days = dpm - d2 + d1 end end if months < 0 then years = years - 1 months = months + 12 end days, time = math.modf(days) local H, M, S = h_m_s(time) return setmetatable({ date1 = date1, date2 = date2, partial = false, -- avoid index lookup years = years, months = months, days = days, hours = H, minutes = M, seconds = S, isnegative = isnegative, iszero = iszero, age = _diff_age, duration = _diff_duration, }, diffmt) end return { _current = current, _Date = Date, _days_in_month = days_in_month, } 48b9402c32798b1e9f91f2ab44283ebda7b53ed9 Template:SDcat 10 413 826 2020-08-07T18:02:31Z wikipedia>MusikBot II 0 Protected "[[Template:SDcat]]": [[Wikipedia:High-risk templates|High-risk template or module]] ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) wikitext text/x-wiki <includeonly>{{#invoke:SDcat |setCat}}</includeonly><noinclude> {{documentation}} </noinclude> 8c6e8783ddb0dc699d6fb60370db97b73725b9a6 Module:Convert 828 496 1004 2020-08-09T22:52:24Z string2>Bsherr 0 adding comment wikitext text/x-wiki <includeonly>{{{{{♥|safesubst:}}}#invoke:convert|convert}}</includeonly><noinclude> {{documentation}} <!-- Add categories to the /doc subpage, interwikis to Wikidata, not here --> </noinclude> 952ac4080dc4a427c4a65db9951c8a4f4c91c626 Module:SDcat 828 415 830 2020-08-16T17:20:37Z wikipedia>RexxS 0 if no connection to Wikidata, then do nothing Scribunto text/plain --[[ SDcat Module to check whether local short description matches that on Wikidata --]] local p = {} ------------------------------------------------------------------------------- --[[ setCat has the qid of a Wikidata entity passed as |qid= (it defaults to the associated qid of the current article if omitted) and the local short description passed as |sd= It returns a category if there is an associated Wikidata entity. It returns one of the following tracking categories, as appropriate: * Category:Short description matches Wikidata (case-insensitive) * Category:Short description is different from Wikidata * Category:Short description with empty Wikidata description For testing purposes, a link prefix |lp= may be set to ":" to make the categories visible. --]] -- function exported for use in other modules -- (local short description, Wikidata entity-ID, link prefix) p._setCat = function(sdesc, itemID, lp) if not mw.wikibase then return nil end if itemID == "" then itemID = nil end -- Wikidata description field local wdesc = (mw.wikibase.getDescription(itemID) or ""):lower() if wdesc == "" then return "[[" .. lp .. "Category:Short description with empty Wikidata description]]" elseif wdesc == sdesc then return "[[" .. lp .. "Category:Short description matches Wikidata]]" else return "[[" .. lp .. "Category:Short description is different from Wikidata]]" end end -- function exported for call from #invoke p.setCat = function(frame) local args if frame.args.sd then args = frame.args else args = frame:getParent().args end -- local short description local sdesc = mw.text.trim(args.sd or ""):lower() -- Wikidata entity-ID local itemID = mw.text.trim(args.qid or "") -- link prefix, strip quotes local lp = mw.text.trim(args.lp or ""):gsub('"', '') return p._setCat(sdesc, itemID, lp) end return p 6c19ac0f72c79a618eb105808f74701376bb2b38 Template:Yesno 10 307 609 2020-08-28T03:15:17Z wikipedia>Xaosflux 0 add additional paramerters, "t", "f" - requested on talk - worked in sandbox /testcases wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#switch: {{<includeonly>safesubst:</includeonly>lc: {{{1|¬}}} }} |no |n |f |false |off |0 = {{{no|<!-- null -->}}} | = {{{blank|{{{no|<!-- null -->}}}}}} |¬ = {{{¬|}}} |yes |y |t |true |on |1 = {{{yes|yes}}} |#default = {{{def|{{{yes|yes}}}}}} }}<noinclude> {{Documentation}} </noinclude> 629c2937bc5cf7cfe13cd2a598582af832782399 Module:TNT 828 360 714 2020-08-30T07:28:25Z wikipedia>Johnuniq 0 Changed protection level for "[[Module:TNT]]": [[WP:High-risk templates|High-risk Lua module]]: per request at [[WP:RFPP]] to match [[Module:Excerpt]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain -- -- INTRO: (!!! DO NOT RENAME THIS PAGE !!!) -- This module allows any template or module to be copy/pasted between -- wikis without any translation changes. All translation text is stored -- in the global Data:*.tab pages on Commons, and used everywhere. -- -- SEE: https://www.mediawiki.org/wiki/Multilingual_Templates_and_Modules -- -- ATTENTION: -- Please do NOT rename this module - it has to be identical on all wikis. -- This code is maintained at https://www.mediawiki.org/wiki/Module:TNT -- Please do not modify it anywhere else, as it may get copied and override your changes. -- Suggestions can be made at https://www.mediawiki.org/wiki/Module_talk:TNT -- -- DESCRIPTION: -- The "msg" function uses a Commons dataset to translate a message -- with a given key (e.g. source-table), plus optional arguments -- to the wiki markup in the current content language. -- Use lang=xx to set language. Example: -- -- {{#invoke:TNT | msg -- | I18n/Template:Graphs.tab <!-- https://commons.wikimedia.org/wiki/Data:I18n/Template:Graphs.tab --> -- | source-table <!-- uses a translation message with id = "source-table" --> -- | param1 }} <!-- optional parameter --> -- -- -- The "doc" function will generate the <templatedata> parameter documentation for templates. -- This way all template parameters can be stored and localized in a single Commons dataset. -- NOTE: "doc" assumes that all documentation is located in Data:Templatedata/* on Commons. -- -- {{#invoke:TNT | doc | Graph:Lines }} -- uses https://commons.wikimedia.org/wiki/Data:Templatedata/Graph:Lines.tab -- if the current page is Template:Graph:Lines/doc -- local p = {} local i18nDataset = 'I18n/Module:TNT.tab' -- Forward declaration of the local functions local sanitizeDataset, loadData, link, formatMessage function p.msg(frame) local dataset, id local params = {} local lang = nil for k, v in pairs(frame.args) do if k == 1 then dataset = mw.text.trim(v) elseif k == 2 then id = mw.text.trim(v) elseif type(k) == 'number' then table.insert(params, mw.text.trim(v)) elseif k == 'lang' and v ~= '_' then lang = mw.text.trim(v) end end return formatMessage(dataset, id, params, lang) end -- Identical to p.msg() above, but used from other lua modules -- Parameters: name of dataset, message key, optional arguments -- Example with 2 params: format('I18n/Module:TNT', 'error_bad_msgkey', 'my-key', 'my-dataset') function p.format(dataset, key, ...) local checkType = require('libraryUtil').checkType checkType('format', 1, dataset, 'string') checkType('format', 2, key, 'string') return formatMessage(dataset, key, {...}) end -- Identical to p.msg() above, but used from other lua modules with the language param -- Parameters: language code, name of dataset, message key, optional arguments -- Example with 2 params: formatInLanguage('es', I18n/Module:TNT', 'error_bad_msgkey', 'my-key', 'my-dataset') function p.formatInLanguage(lang, dataset, key, ...) local checkType = require('libraryUtil').checkType checkType('formatInLanguage', 1, lang, 'string') checkType('formatInLanguage', 2, dataset, 'string') checkType('formatInLanguage', 3, key, 'string') return formatMessage(dataset, key, {...}, lang) end -- Obsolete function that adds a 'c:' prefix to the first param. -- "Sandbox/Sample.tab" -> 'c:Data:Sandbox/Sample.tab' function p.link(frame) return link(frame.args[1]) end function p.doc(frame) local dataset = 'Templatedata/' .. sanitizeDataset(frame.args[1]) return frame:extensionTag('templatedata', p.getTemplateData(dataset)) .. formatMessage(i18nDataset, 'edit_doc', {link(dataset)}) end function p.getTemplateData(dataset) -- TODO: add '_' parameter once lua starts reindexing properly for "all" languages local data = loadData(dataset) local names = {} for _, field in pairs(data.schema.fields) do table.insert(names, field.name) end local params = {} local paramOrder = {} for _, row in pairs(data.data) do local newVal = {} local name = nil for pos, val in pairs(row) do local columnName = names[pos] if columnName == 'name' then name = val else newVal[columnName] = val end end if name then params[name] = newVal table.insert(paramOrder, name) end end -- Work around json encoding treating {"1":{...}} as an [{...}] params['zzz123']='' local json = mw.text.jsonEncode({ params=params, paramOrder=paramOrder, description=data.description }) json = string.gsub(json,'"zzz123":"",?', "") return json end -- Local functions sanitizeDataset = function(dataset) if not dataset then return nil end dataset = mw.text.trim(dataset) if dataset == '' then return nil elseif string.sub(dataset,-4) ~= '.tab' then return dataset .. '.tab' else return dataset end end loadData = function(dataset, lang) dataset = sanitizeDataset(dataset) if not dataset then error(formatMessage(i18nDataset, 'error_no_dataset', {})) end -- Give helpful error to thirdparties who try and copy this module. if not mw.ext or not mw.ext.data or not mw.ext.data.get then error('Missing JsonConfig extension; Cannot load https://commons.wikimedia.org/wiki/Data:' .. dataset) end local data = mw.ext.data.get(dataset, lang) if data == false then if dataset == i18nDataset then -- Prevent cyclical calls error('Missing Commons dataset ' .. i18nDataset) else error(formatMessage(i18nDataset, 'error_bad_dataset', {link(dataset)})) end end return data end -- Given a dataset name, convert it to a title with the 'commons:data:' prefix link = function(dataset) return 'c:Data:' .. mw.text.trim(dataset or '') end formatMessage = function(dataset, key, params, lang) for _, row in pairs(loadData(dataset, lang).data) do local id, msg = unpack(row) if id == key then local result = mw.message.newRawMessage(msg, unpack(params or {})) return result:plain() end end if dataset == i18nDataset then -- Prevent cyclical calls error('Invalid message key "' .. key .. '"') else error(formatMessage(i18nDataset, 'error_bad_msgkey', {key, link(dataset)})) end end return p 9d0d10e54abd232c806dcabccaf03e52858634a1 Module:Effective protection level 828 355 704 2020-09-29T03:38:47Z wikipedia>Jackmcbarn 0 bring in changes from sandbox Scribunto text/plain local p = {} -- Returns the permission required to perform a given action on a given title. -- If no title is specified, the title of the page being displayed is used. function p._main(action, pagename) local title if type(pagename) == 'table' and pagename.prefixedText then title = pagename elseif pagename then title = mw.title.new(pagename) else title = mw.title.getCurrentTitle() end pagename = title.prefixedText if action == 'autoreview' then local level = mw.ext.FlaggedRevs.getStabilitySettings(title) level = level and level.autoreview if level == 'review' then return 'reviewer' elseif level ~= '' then return level else return nil -- not '*'. a page not being PC-protected is distinct from it being PC-protected with anyone able to review. also not '', as that would mean PC-protected but nobody can review end elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' and action ~= 'undelete' then error( 'First parameter must be one of edit, move, create, upload, undelete, autoreview', 2 ) end if title.namespace == 8 then -- MediaWiki namespace if title.text:sub(-3) == '.js' or title.text:sub(-4) == '.css' or title.contentModel == 'javascript' or title.contentModel == 'css' then -- site JS or CSS page return 'interfaceadmin' else -- any non-JS/CSS MediaWiki page return 'sysop' end elseif title.namespace == 2 and title.isSubpage then if title.contentModel == 'javascript' or title.contentModel == 'css' then -- user JS or CSS page return 'interfaceadmin' elseif title.contentModel == 'json' then -- user JSON page return 'sysop' end end if action == 'undelete' then return 'sysop' end local level = title.protectionLevels[action] and title.protectionLevels[action][1] if level == 'sysop' or level == 'editprotected' then return 'sysop' elseif title.cascadingProtection.restrictions[action] and title.cascadingProtection.restrictions[action][1] then -- used by a cascading-protected page return 'sysop' elseif level == 'templateeditor' then return 'templateeditor' elseif action == 'move' then local blacklistentry = mw.ext.TitleBlacklist.test('edit', pagename) -- Testing action edit is correct, since this is for the source page. The target page name gets tested with action move. if blacklistentry and not blacklistentry.params.autoconfirmed then return 'templateeditor' elseif title.namespace == 6 then return 'filemover' elseif level == 'extendedconfirmed' then return 'extendedconfirmed' else return 'autoconfirmed' end end local blacklistentry = mw.ext.TitleBlacklist.test(action, pagename) if blacklistentry then if not blacklistentry.params.autoconfirmed then return 'templateeditor' elseif level == 'extendedconfirmed' then return 'extendedconfirmed' else return 'autoconfirmed' end elseif level == 'editsemiprotected' then -- create-semiprotected pages return this for some reason return 'autoconfirmed' elseif level then return level elseif action == 'upload' then return 'autoconfirmed' elseif action == 'create' and title.namespace % 2 == 0 and title.namespace ~= 118 then -- You need to be registered, but not autoconfirmed, to create non-talk pages other than drafts return 'user' else return '*' end end setmetatable(p, { __index = function(t, k) return function(frame) return t._main(k, frame.args[1]) end end }) return p 70256a489edf6be9808031b14a7e3ef3e025da97 Module:For 828 504 1014 2020-11-02T22:29:56Z string2>Andrybak 0 don't add talk pages and pages in the user namespace to [[:Category:Hatnote templates with errors]] – this is similar to [[Special:Diff/953627233/986768317|Module:Other uses of]] and [[Special:Diff/967664662|Module:Hatnote]] Scribunto text/plain local mArguments --initialize lazily local mHatlist = require('Module:Hatnote list') local mHatnote = require('Module:Hatnote') local yesNo = require('Module:Yesno') local p = {} --Implements {{For}} from the frame --uses capitalized "For" to avoid collision with Lua reserved word "for" function p.For (frame) mArguments = require('Module:Arguments') return p._For(mArguments.getArgs(frame)) end --Implements {{For}} but takes a manual arguments table function p._For (args) local use = args[1] if (not use) then return mHatnote.makeWikitextError( 'no context parameter provided. Use {{other uses}} for "other uses" hatnotes.', 'Template:For#Errors', args.category ) end local pages = {} function two (a, b) return a, b, 1 end --lets us run ipairs from 2 for k, v in two(ipairs(args)) do table.insert(pages, v) end local title = mw.title.getCurrentTitle() local skipCat = title.isTalkPage or title.namespace == 2 --don't categorise talk pages and userspace local oddCat = skipCat and '' or '[[Category:Hatnote templates using unusual parameters]]' local category = yesNo(args.category) return mHatnote._hatnote( mHatlist.forSeeTableToString({{use = use, pages = pages}}), {selfref = args.selfref} ) .. ( (use == 'other uses') and ((category == true) or (category == nil)) and oddCat or '' ) end return p 2faf1bd8c657f3425af829cbde9733a936d8d4f4 1012 2021-07-17T12:58:12Z string2>Plastikspork 0 [[Wikipedia:Templates for discussion/Log/2021 July 8#Template:For]] closed as do not merge ([[WP:XFDC#4.0.12|XFDcloser]]) wikitext text/x-wiki <includeonly>{{#invoke:For|For}}</includeonly><noinclude> {{Documentation}} </noinclude> 3f70c0fa7cd736071e7c6e7dcd90ff3704df26bb Template:Documentation/styles.css 10 327 649 2020-11-19T20:21:58Z wikipedia>Izno 0 Changed protection level for "[[Module:Documentation/styles.css]]": actually match module ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) text text/plain /* {{pp|small=yes}} */ .documentation, .documentation-metadata { border: 1px solid #a2a9b1; background-color: #ecfcf4; clear: both; } .documentation { margin: 1em 0 0 0; padding: 1em; } .documentation-metadata { margin: 0.2em 0; /* same margin left-right as .documentation */ font-style: italic; padding: 0.4em 1em; /* same padding left-right as .documentation */ } .documentation-startbox { padding-bottom: 3px; border-bottom: 1px solid #aaa; margin-bottom: 1ex; } .documentation-heading { font-weight: bold; font-size: 125%; } .documentation-clear { /* Don't want things to stick out where they shouldn't. */ clear: both; } .documentation-toolbar { font-style: normal; font-size: 85%; } ce0e629c92e3d825ab9fd927fe6cc37d9117b6cb Module:Documentation/styles.css 828 362 718 2020-11-19T20:21:58Z wikipedia>Izno 0 Changed protection level for "[[Module:Documentation/styles.css]]": actually match module ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) text text/plain /* {{pp|small=yes}} */ .documentation, .documentation-metadata { border: 1px solid #a2a9b1; background-color: #ecfcf4; clear: both; } .documentation { margin: 1em 0 0 0; padding: 1em; } .documentation-metadata { margin: 0.2em 0; /* same margin left-right as .documentation */ font-style: italic; padding: 0.4em 1em; /* same padding left-right as .documentation */ } .documentation-startbox { padding-bottom: 3px; border-bottom: 1px solid #aaa; margin-bottom: 1ex; } .documentation-heading { font-weight: bold; font-size: 125%; } .documentation-clear { /* Don't want things to stick out where they shouldn't. */ clear: both; } .documentation-toolbar { font-style: normal; font-size: 85%; } ce0e629c92e3d825ab9fd927fe6cc37d9117b6cb Template:Tlx 10 312 619 2020-11-20T18:53:35Z wikipedia>Primefac 0 Primefac moved page [[Template:Tlx]] to [[Template:Template link expanded]] over redirect: expand name, make it more obvious wikitext text/x-wiki #REDIRECT [[Template:Template link expanded]] {{Redirect category shell| {{R from move}} }} 1fec988ceb46cb324af228aac45d7cd25fcc9008 Template:Template link expanded 10 313 621 2020-11-21T12:04:41Z wikipedia>Primefac 0 update wikitext text/x-wiki {{#Invoke:Template link general|main|code=on}}<noinclude> {{Documentation|1=Template:Tlg/doc |content = {{tlg/doc|tlx}} }} <!-- Add categories to the /doc subpage, not here! --> </noinclude> 6c99696fee02f1da368ed20d2504e19bc15b1c13 Template:Template link with link off 10 330 655 2020-11-21T12:06:17Z wikipedia>Primefac 0 update wikitext text/x-wiki <includeonly>{{#Invoke:Template link general|main|nowrap=yes|nolink=yes}}</includeonly><noinclude> {{Documentation|1=Template:Tlg/doc |content = {{tlg/doc|tlf}} }} <!-- Add categories to the /doc subpage, not here! --> </noinclude> b099fea5d1f36b0b4b9cb253ad3a9f4e095f6851 Module:Uses TemplateStyles/doc 828 390 780 2020-12-29T16:09:34Z wikipedia>Izno 0 uses data wikitext text/x-wiki {{Lua|Module:Uses TemplateStyles/config|Module:Yesno|Module:List|Module:TableTools|Module:Message box|Module:TNT}} <!-- uses data [[c:Data:I18n/Uses_TemplateStyles.tab]] --> Implements {{tl|Uses TemplateStyles}} 9374abbfb0d2232c7218828ac2d9727c43e6e04c Module:Video game reviews/styles.css 828 471 942 2021-01-01T18:52:46Z wikipedia>Izno 0 pp temp text text/plain /* {{pp|small=yes}} */ .video-game-reviews { float: right; clear: right; margin: 0 1em 1em; text-align: center; padding: 0; } /* As with width below, .vgr-left and .vgr-none maybe shouldn't exist. Someone who really needs this to be left rather than right can always plop it in its own div... */ .vgr-left { float: left; clear: left; margin: 0 1em 1em 0; } .vgr-none { float: none; clear: left; margin: 0 1em 1em 0; } .vgr-single { width: 23em; } .vgr-edit-on-wikidata { border: 1px solid #a2a9b1; border-top: none; padding: 0.2em; /* Revisit background color because Wikidata link is only about 4.0 contrast. Something for the talk page. */ background: #d1dbdf; font-size: 88%; font-weight: bold; } .video-game-reviews table { border: 1px solid #a2a9b1; /* @noflip */ margin: 0; font-size: 88%; width: 100%; } .video-game-reviews td, .video-game-reviews th, .video-game-reviews caption { border: 1px solid #a2a9b1; vertical-align: middle; } .video-game-reviews caption { border-bottom: none; background: #d1dbdf; text-align: center; padding: 0.2em 0.4em; } .video-game-reviews th { background: #eaecf0; } .vgr-awards td { background: #f2f2f2; } .vgr-hrow th { background: #e8f4f8; } .video-game-reviews .table-na { color: #707070 } .vgr-reviews, .vgr-reviews tr:last-child td, .vgr-reviews tr:last-child th { border-bottom: none; } .vgr-title, .vgr-subtitle, .vgr-awards tr td:first-child { font-weight: bold; } .mw-collapsed .vgr-title { display: inline; /* so that it doesn't "wrap" to the next line */ } .video-game-reviews table tr td:first-child, .vgr-awards td { text-align: left; } .video-game-reviews table tr td.vgr-center { /* Needed to override text-align left above in one particular case where it looks prettier. Usually it would make more sense to make a semantic name */ text-align: center; } .video-game-reviews .vgr-stacked { border-top: none; } @media (max-width: 720px) { .video-game-reviews { width: 100%; float: none; /* set float/clear explicitly to override earlier */ clear: both; margin: 0; } .video-game-reviews table { display: table; /* Minerva is silly */ } .video-game-reviews caption { display: table-caption; /* Minerva is still silly */ } } 638393c71fab9b2fc8e15113df813beecb4d842e Module:Xt 828 501 1002 2021-01-14T19:30:02Z string2>Xaosflux 0 Changed protection level for "[[Template:Xt]]": used in system messages ([[MediaWiki:Titleblacklist-custom-archive]]) ([Edit=Require template editor access] (indefinite) [Move=Require administrator access] (indefinite)) wikitext text/x-wiki {{#ifeq:{{NAMESPACE}}|{{ns:0}}|{{FormattingError|[[:{{#invoke:TEMPLATENAME|main}}]] is only for examples of style and formatting. Do not use it in actual articles.}}|<span class="example" style="font-family: Georgia, 'DejaVu Serif', serif; color: #006400;" {{#if:{{{title|}}}|title="{{{title}}}"}}>{{{1|Example text}}}</span>}}<noinclude> {{Documentation}} </noinclude> e8c5895953384f68b9648a698f7f33d79748e408 Template:Original research 10 464 928 2021-01-20T19:57:38Z wikipedia>Elli 0 add reason parameter per edit req wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{Ambox | name = Original research | subst = <includeonly>{{subst:substcheck}}</includeonly> | type = content | class = ambox-Original_research | small = {{{small|}}} | issue = This {{{part|{{{1|article}}}}}} '''possibly contains [[Wikipedia:No original research|original research]]'''. {{#if:{{{reason|}}}|{{{reason|}}}}} | fix = Please [{{fullurl:{{FULLPAGENAME}}|action=edit}} improve it] by [[WP:Verifiability|verifying]] the claims made and adding [[Wikipedia:Citing sources#Inline citations|inline citations]]. Statements consisting only of original research should be removed. | removalnotice = yes | talk = {{{discuss|}}} | cat = Articles that may contain original research | all = All articles that may contain original research | date = {{{date|}}} }} }}<noinclude> {{Documentation}} </noinclude> c6ee867ff0fb8652b09b606114fadaa57737aeb4 Template:Tl 10 305 605 2021-02-12T22:03:00Z wikipedia>Anthony Appleyard 0 Anthony Appleyard moved page [[Template:Tl]] to [[Template:Template link]]: [[Special:Permalink/1006428669|Requested]] by Buidhe at [[WP:RM/TR]]: RM closed as move wikitext text/x-wiki #REDIRECT [[Template:Template link]] {{Redirect category shell| {{R from move}} }} d6593bb3b4a866249f55d0f34b047a71fe1f1529 Module:Tl 828 347 688 2021-02-12T22:03:00Z wikipedia>Anthony Appleyard 0 Anthony Appleyard moved page [[Template:Tl]] to [[Template:Template link]]: [[Special:Permalink/1006428669|Requested]] by Buidhe at [[WP:RM/TR]]: RM closed as move wikitext text/x-wiki #REDIRECT [[Template:Template link]] {{Redirect category shell| {{R from move}} }} d6593bb3b4a866249f55d0f34b047a71fe1f1529 Template:Div col 10 321 637 2021-02-14T23:20:57Z wikipedia>Matt Fitzpatrick 0 whitelist parameter class wikitext text/x-wiki <includeonly><templatestyles src="Div col/styles.css"/><!-- --><div class="div-col {{#ifeq:{{{small|}}}|yes|div-col-small}} {{#ifeq:{{{rules|}}}|yes|div-col-rules}} {{{class|}}}" <!-- -->{{#if:{{{colwidth|}}}{{{gap|}}}{{{style|}}}|<!-- -->style="{{#if:{{{colwidth|}}}|column-width: {{{colwidth}}};}}{{#if:{{{gap|}}}|column-gap: {{{gap}}};}}{{#if:{{{style|}}}|{{{style}}}}}"<!-- -->}}><!-- -->{{#if:{{{content|}}}|{{{content}}}</div>}}<!-- Inventory how many pages use small=yes -->{{#ifeq:{{{small|}}}|yes|[[Category:Pages using div col with small parameter]]}}<!-- --></includeonly>{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using div col with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Div col]] with unknown parameter "_VALUE_"; use colwidth= to specify column size |ignoreblank=y | class | colwidth | content | gap | rules | small | style }}<noinclude> {{Documentation}} </noinclude> 6e84133dd867d6c701e7b161878cf66665bb7eb7 Template:Reflist/styles.css 10 424 848 2021-03-08T23:00:26Z wikipedia>Izno 0 Changed protection level for "[[Template:Reflist/styles.css]]": match parent ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) text text/plain /* {{pp|small=yes}} */ /* can we remove the font size declarations? .references gets a font-size in * common.css that is always 90, and there is nothing else in reflist out in * the wild. May affect column sizes. */ .reflist { font-size: 90%; /* Default font-size */ margin-bottom: 0.5em; list-style-type: decimal; } .reflist .references { font-size: 100%; /* Reset font-size when nested in div.reflist */ margin-bottom: 0; /* Avoid double margin when nested in div.reflist */ list-style-type: inherit; /* Enable custom list style types */ } /* columns-2 and columns-3 are legacy for "2 or more" column view from when the * template was implemented with column-count. */ .reflist-columns-2 { column-width: 30em; } .reflist-columns-3 { column-width: 25em; } /* Reset top margin for lists embedded in columns */ .reflist-columns { margin-top: 0.3em; } .reflist-columns ol { margin-top: 0; } /* Avoid elements breaking between columns */ .reflist-columns li { page-break-inside: avoid; /* Removed from CSS in favor of break-inside c. 2020 */ break-inside: avoid-column; } .reflist-upper-alpha { list-style-type: upper-alpha; } .reflist-upper-roman { list-style-type: upper-roman; } .reflist-lower-alpha { list-style-type: lower-alpha; } .reflist-lower-greek { list-style-type: lower-greek; } .reflist-lower-roman { list-style-type: lower-roman; } 531a26d48f0e7826c61f764cfb7d5fb200032c34 Template:EditOnWikidata 10 455 910 2021-03-17T16:57:05Z wikipedia>MSGJ 0 MSGJ moved page [[Template:EditOnWikidata]] to [[Template:Edit on Wikidata]]: add spaces wikitext text/x-wiki #REDIRECT [[Template:Edit on Wikidata]] {{Redirect category shell| {{R from move}} }} da7377f33f3901e6962fbca394dbf9adbae353a6 Template:Edit on Wikidata 10 456 912 2021-03-17T16:57:05Z wikipedia>MSGJ 0 MSGJ moved page [[Template:EditOnWikidata]] to [[Template:Edit on Wikidata]]: add spaces wikitext text/x-wiki {{#switch: {{{noicon|}}} | no = | false = | #default = <div class="metadata" style="text-align: right;">&#91;[[d:{{#if:{{{qid|}}}|{{{qid}}}|{{#invoke:WikidataIB|pageId}}}}|edit on Wikidata]]]</div> }}<noinclude> {{Documentation}}</noinclude> 18e6ce3f005ce4f424aeba196432645ded6036bb Template:Template link 10 306 607 2021-03-25T19:03:22Z wikipedia>Izno 0 [[Wikipedia:Templates for discussion/Log/2021 March 18#Template:Tlu]] closed as keep ([[WP:XFDC#4.0.11|XFDcloser]]) wikitext text/x-wiki &#123;&#123;[[Template:{{{1}}}|{{{1}}}]]&#125;&#125;<noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> eabbec62efe3044a98ebb3ce9e7d4d43c222351d Module:Template link 828 348 690 2021-03-25T19:03:22Z wikipedia>Izno 0 [[Wikipedia:Templates for discussion/Log/2021 March 18#Template:Tlu]] closed as keep ([[WP:XFDC#4.0.11|XFDcloser]]) wikitext text/x-wiki &#123;&#123;[[Template:{{{1}}}|{{{1}}}]]&#125;&#125;<noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> eabbec62efe3044a98ebb3ce9e7d4d43c222351d Module:If preview/configuration 828 432 864 2021-05-05T18:56:00Z wikipedia>Izno 0 Protected "[[Module:If preview/configuration]]": match parent ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain --[[ We perform the actual check for whether this is a preview here since preprocessing is relatively expensive. ]] local frame = mw.getCurrentFrame() local function is_preview() local revision_id = frame:preprocess('{{REVISIONID}}') -- {{REVISIONID}} is usually the empty string when previewed. -- I don't know why we're checking for nil but hey, maybe someday things -- would have broken return revision_id == nil or revision_id == '' end local function templatestyles() return frame:extensionTag{ name = 'templatestyles', args = { src = 'Module:If preview/styles.css' } } end return { preview = is_preview(), templatestyles = templatestyles(), warning_infrastructure = '%s<div class="preview-warning"><strong>Preview warning:</strong> %s</div>', missing_warning = 'The template has no warning text. Please add a warning.' } 3edc8897c51a61b9e710b2a4d9eb657b3c2f1034 Module:If preview/styles.css 828 433 866 2021-05-05T18:56:47Z wikipedia>Izno 0 pp text text/plain /* {{pp|small=yes}} */ .preview-warning { font-style: italic; /* @noflip */ padding-left: 1.6em; margin-bottom: 0.5em; color: red; } /* The templatestyles element inserts a link element before hatnotes. * TODO: Remove link if/when WMF resolves T200206 */ .preview-warning + link + .preview-warning { margin-top: -0.5em; } 8b79ffc4853d424a805b084de00030e04bbd573e Module:If preview 828 431 862 2021-05-05T19:01:42Z wikipedia>Izno 0 merge in functionality from [[Module:Preview warning]] and add TemplateStyles, remove p.boolean as not necessary, move some stuff into a cfg page, and don't return temporaries in p.main, remove use Scribunto text/plain local p = {} local cfg = mw.loadData('Module:If preview/configuration') --[[ main This function returns either the first argument or second argument passed to this module, depending on whether the page is being previewed. ]] function p.main(frame) if cfg.preview then return frame.args[1] or '' else return frame.args[2] or '' end end --[[ pmain This function returns either the first argument or second argument passed to this module's parent (i.e. template using this module), depending on whether it is being previewed. ]] function p.pmain(frame) return p.main(frame:getParent()) end local function warning_text(warning) return mw.ustring.format( cfg.warning_infrastructure, cfg.templatestyles, warning ) end function p._warning(args) local warning = args[1] and args[1]:match('^%s*(.-)%s*$') or '' if warning == '' then return warning_text(cfg.missing_warning) end if not cfg.preview then return '' end return warning_text(warning) end --[[ warning This function returns a "preview warning", which is the first argument marked up with HTML and some supporting text, depending on whether the page is being previewed. disabled since we'll implement the template version in general ]] --function p.warning(frame) -- return p._warning(frame.args) --end --[[ warning, but for pass-through templates like {{preview warning}} ]] function p.pwarning(frame) return p._warning(frame:getParent().args) end return p 9a92196d0001b8016f2501aedfadcc3adcb974ef Module:CountryData 828 461 922 2021-05-05T20:40:05Z wikipedia>GKFX 0 Add optional caching layer for most-used templates via loadData. Reduces rendering time as tested on [[List_of_twin_towns_and_sister_cities_in_Germany]] Scribunto text/plain local p = {} local mostUsed = mw.loadData('Module:CountryData/summary') local function getcontents(frame,country,params) return frame:expandTemplate({title="Country data "..country;args=params}) end function p.getcachedtable(frame, country, params) country = mostUsed.redirects[country] or country if params and next(params) then return p.gettable(frame, country, params) end -- Uses mw.loadData to cache data for the most-used templates if mostUsed.pages[country] then local cache = mw.loadData('Module:CountryData/cache' .. mostUsed.pages[country]) if cache.data[country] then return cache.data[country] end end -- if not in cache return p.gettable(frame, country, params) end function p.gettable(frame,country,params) --Returns the parameters of a country data template as a Lua table --If not a valid data template, return empty table local bool, s = pcall(getcontents,frame,country,params or {}) if bool and (string.find(s,"^%{%{ *%{%{%{1") or string.find(s,"^%{%{safesubst: *%{%{%{1")) then --Replace parameter delimiters with arbitrary control characters --to avoid clashes if param values contain equals/pipe signs s = string.gsub(s,"|([^|=]-)=","\1\1%1\2") s = string.gsub(s,"}}%s*$","\1") --Loop over string and add params to table local part = {} for par in string.gmatch(s,"\1[^\1\2]-\2[^\1\2]-\1") do local k = string.match(par,"\1%s*(.-)%s*\2") local v = string.match(par,"\2%s*(.-)%s*\1") if v and not (v=="" and string.find(k,"^flag alias")) then part[k] = v end end return part else return {} end end function p.getalias(frame) --Returns a single parameter value from a data template local part = p.gettable(frame,frame.args[1]) if frame.args.variant then return tostring(part[frame.args[2].."-"..frame.args.variant] or part[frame.args[2]] or frame.args.def) else return tostring(part[frame.args[2]] or frame.args.def) end end function p.gettemplate(frame) --For testing, recreates the country data from the created Lua table --Get data table local data = p.gettable(frame,frame.args[1]) --Concatenate fields into a template-like string local out = "{{ {{{1}}}" for k,v in pairs(data) do out = out.."\n| "..k.." = "..v end return out.."\n}}" end return p 69ae4a41658bbbdd1c559e87420fcbd21eb437f4 Module:CountryData/summary 828 462 924 2021-05-06T17:59:48Z wikipedia>MusikBot II 0 Protected "[[Module:CountryData/summary]]": [[Wikipedia:High-risk templates|High-risk template or module]] ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain local p = {} p.pages = {Afghanistan='K', Albania='H', Algeria='F', Angola='K', Argentina='B', Armenia='G', Australia='A', Austria='B', Azerbaijan='F', Bahrain='K', Bangladesh='I', Belarus='D', Belgium='B', Bolivia='J', Bosnia_and_Herzegovina='F', Brazil='B', Bulgaria='D', Cameroon='I', Canada='A', Chile='D', China='B', Chinese_Taipei='H', Colombia='D', Costa_Rica='I', Croatia='D', Cuba='G', Cyprus='G', Czech_Republic='B', Czechoslovakia='H', Democratic_Republic_of_the_Congo='K', Denmark='C', Dominican_Republic='H', East_Germany='K', Ecuador='G', Egypt='E', El_Salvador='J', England='B', Estonia='E', Ethiopia='J', Fiji='K', Finland='C', France='A', Germany='A', Ghana='G', Great_Britain='C', Greece='D', Guatemala='J', Honduras='K', Hong_Kong='F', Hungary='C', Iceland='G', India='A', Indonesia='E', Iran='B', Iraq='J', Ireland='E', Israel='D', Italy='A', Ivory_Coast='I', Jamaica='G', Japan='A', Jordan='K', Kazakhstan='E', Kenya='H', Kuwait='K', Latvia='F', Lebanon='J', Lithuania='F', Luxembourg='G', Malaysia='E', Mali='K', Malta='I', Mexico='C', Moldova='I', Montenegro='H', Morocco='F', Myanmar='J', Nepal='I', Netherlands='A', New_Zealand='C', Nigeria='E', North_Korea='J', North_Macedonia='I', Northern_Ireland='J', Norway='C', Pakistan='F', Panama='I', Paraguay='G', Peru='E', Philippines='E', Poland='A', Portugal='C', Puerto_Rico='H', Qatar='I', Republic_of_Ireland='F', Romania='C', Russia='A', Saudi_Arabia='H', Scotland='D', Senegal='I', Serbia='D', Singapore='F', Slovakia='D', Slovenia='D', South_Africa='C', South_Korea='C', Soviet_Union='E', Spain='A', Sri_Lanka='H', Sweden='B', Switzerland='B', Syria='J', Taiwan='K', Tanzania='K', Thailand='D', Trinidad_and_Tobago='J', Tunisia='G', Turkey='B', Uganda='J', Ukraine='C', United_Arab_Emirates='H', United_Kingdom='B', United_States='A', Uruguay='F', Uzbekistan='H', Venezuela='E', Vietnam='G', Wales='E', West_Germany='G', Yugoslavia='H', Zimbabwe='I', ['Georgia_(country)']='F'} p.redirects = {AFG='Afghanistan', ALB='Albania', ALG='Algeria', AND='Andorra', ANG='Angola', ARG='Argentina', ARM='Armenia', ARU='Aruba', ATG='Antigua and Barbuda', AUS='Australia', AUT='Austria', AZE='Azerbaijan', BAH='Bahamas', BAN='Bangladesh', BAR='Barbados', BDI='Burundi', BEL='Belgium', BEN='Benin', BER='Bermuda', BFA='Burkina Faso', BHR='Bahrain', BIH='Bosnia and Herzegovina', BLR='Belarus', BOL='Bolivia', BOT='Botswana', BRA='Brazil', BRU='Brunei', BUL='Bulgaria', BWA='Botswana', CAM='Cambodia', CAN='Canada', CAY='Cayman Islands', CGO='Republic of the Congo', CHE='Switzerland', CHI='Chile', CHL='Chile', CHN='China', CIS='Commonwealth of Independent States', CIV='Ivory Coast', CMR='Cameroon', COD='Democratic Republic of the Congo', COK='Cook Islands', COL='Colombia', CPV='Cape Verde', CRC='Costa Rica', CRO='Croatia', CSA='Confederate States of America', CSK='Czechoslovakia', CUB='Cuba', CUR='Curaçao', CYP='Cyprus', CZE='Czech Republic', Congo='Republic of the Congo', DEN='Denmark', DEU='Germany', DNK='Denmark', DOM='Dominican Republic', DRC='Democratic Republic of the Congo', ECU='Ecuador', EGY='Egypt', ENG='England', ESA='El Salvador', ESP='Spain', EST='Estonia', ETH='Ethiopia', EU='European Union', FIJ='Fiji', FIN='Finland', FJI='Fiji', FRA='France', FRG='West Germany', FRO='Faroe Islands', GAB='Gabon', GAM='Gambia', GBR='Great Britain', GDR='East Germany', GEO='Georgia (country)', GER='Germany', GHA='Ghana', GIB='Gibraltar', GRC='Greece', GRE='Greece', GRN='Grenada', GUA='Guatemala', GUI='Guinea', GUM='Guam', GUY='Guyana', Georgia='Georgia (country)', HAI='Haiti', HKG='Hong Kong', HON='Honduras', HRV='Croatia', HUN='Hungary', IDN='Indonesia', INA='Indonesia', IND='India', IRE='Ireland', IRI='Iran', IRL='Republic of Ireland', IRN='Iran', IRQ='Iraq', ISL='Iceland', ISR='Israel', ITA='Italy', JAM='Jamaica', JOR='Jordan', JP='Japan', JPN='Japan', KAZ='Kazakhstan', KEN='Kenya', KGZ='Kyrgyzstan', KOR='South Korea', KOS='Kosovo', KSA='Saudi Arabia', KUW='Kuwait', LAO='Laos', LAT='Latvia', LBN='Lebanon', LBR='Liberia', LBY='Libya', LCA='Saint Lucia', LIB='Lebanon', LIE='Liechtenstein', LIT='Lithuania', LTU='Lithuania', LUX='Luxembourg', LVA='Latvia', MAC='Macau', MAD='Madagascar', MAR='Morocco', MAS='Malaysia', MCO='Monaco', MDA='Moldova', MDV='Maldives', MEX='Mexico', MGL='Mongolia', MKD='North Macedonia', MLI='Mali', MLT='Malta', MNE='Montenegro', MON='Monaco', MOZ='Mozambique', MRI='Mauritius', MYA='Myanmar', MYS='Malaysia', NAM='Namibia', NCA='Nicaragua', NCL='New Caledonia', NED='Netherlands', NEP='Nepal', NGA='Nigeria', NGR='Nigeria', NIC='Nicaragua', NIG='Niger', NIR='Northern Ireland', NLD='Netherlands', NOR='Norway', NZ='New Zealand', NZL='New Zealand', OMA='Oman', PAK='Pakistan', PAN='Panama', PAR='Paraguay', PER='Peru', PHI='Philippines', PHL='Philippines', PLE='Palestine', PNG='Papua New Guinea', POL='Poland', POR='Portugal', PRC='China', PRI='Puerto Rico', PRK='North Korea', PRT='Portugal', PUR='Puerto Rico', QAT='Qatar', ROC='Republic of China', ROM='Romania', ROU='Romania', RSA='South Africa', RUS='Russia', RWA='Rwanda', SAM='Samoa', SCG='Serbia and Montenegro', SCO='Scotland', SEN='Senegal', SER='Serbia', SGP='Singapore', SIN='Singapore', SKN='Saint Kitts and Nevis', SLE='Sierra Leone', SLO='Slovenia', SLV='El Salvador', SMR='San Marino', SPA='Spain', SRB='Serbia', SRI='Sri Lanka', SUD='Sudan', SUI='Switzerland', SUR='Suriname', SVK='Slovakia', SVN='Slovenia', SWE='Sweden', SWI='Switzerland', SYR='Syria', TAN='Tanzania', TCH='Czechoslovakia', THA='Thailand', TJK='Tajikistan', TKM='Turkmenistan', TOG='Togo', TON='Tonga', TPE='Chinese Taipei', TRI='Trinidad and Tobago', TTO='Trinidad and Tobago', TUN='Tunisia', TUR='Turkey', TWN='Taiwan', UAE='United Arab Emirates', UGA='Uganda', UK='United Kingdom', UKGBI='United Kingdom of Great Britain and Ireland', UKR='Ukraine', UN='United Nations', URS='Soviet Union', URU='Uruguay', US='United States', USA='United States', USSR='Soviet Union', UZB='Uzbekistan', VAN='Vanuatu', VEN='Venezuela', VIE='Vietnam', WAL='Wales', WIN='West Indies', Washington='Washington (state)', YEM='Yemen', YUG='Yugoslavia', ZAF='South Africa', ZAM='Zambia', ZIM='Zimbabwe', ['Côte d\'Ivoire']='Ivory Coast', ['DR Congo']='Democratic Republic of the Congo', ['New York']='New York (state)', ['People\'s Republic of China']='China', ['SFR Yugoslavia']='Yugoslavia', ['Timor-Leste']='East Timor', ['United States Virgin Islands']='U.S. Virgin Islands', ['United States of America']='United States'} return p 083be15b6e411ffe1f6456c148ba0dbba7ccb330 Module:Markup/row 828 515 1036 2021-05-27T17:27:33Z string2>Altercari 0 changing background colour to match <pre> wikitext text/x-wiki <noinclude>{| style="border-width:medium;"</noinclude><includeonly>{{#if:{{{c1|value}}}|&#32; {{!-}} {{!}}style{{=}}"background:#f8f9fa ; border:1px solid #eaecf0 ; padding:9px 5px 5px; vertical-align:top;"{{!}} <!-- -->{{#tag:pre |{{{c1|[markup]}}}|style="margin:0;border:none;padding:0; word-wrap:break-word; white-space:-moz-pre-wrap;white-space:-o-pre-wrap;white-space:-pre-wrap;white-space:pre-wrap; {{{c1style|}}}"}} {{!}}style{{=}}"background:#f8f9fa ; border:1px solid #eaecf0 ; padding:5px; vertical-align:top;"{{!}} <!-- --><div style="{{{c2style|}}}"> {{{c2|[''rendering'']}}} </div> }}</includeonly><!-- --><noinclude> |- |style="border-width:1px;border-style:solid none none none;border-color:#ddd; padding:5px; vertical-align:text-top;"| |style="border-width:1px;border-style:solid none none none;border-color:#ddd; padding:5px; vertical-align:text-top;"| |- |}</noinclude> 2636425c7478cebf61528d5eb4cab52b8e695ec9 Template:Category link with count 10 337 669 2021-06-11T18:13:44Z wikipedia>GKFX 0 Support wider range of (valid) input format wikitext text/x-wiki [[:Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|<!-- -->{{#if:{{{name|}}}|{{{name}}}|Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}}}<!-- -->]]&nbsp;({{PAGESINCATEGORY:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|{{{2|all}}}}})<noinclude> {{Documentation}} </noinclude> f93f1540b8c157703bd6d24ae35c35bef745981d Module:Category link with count 828 385 770 2021-06-11T18:13:44Z wikipedia>GKFX 0 Support wider range of (valid) input format wikitext text/x-wiki [[:Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|<!-- -->{{#if:{{{name|}}}|{{{name}}}|Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}}}<!-- -->]]&nbsp;({{PAGESINCATEGORY:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|{{{2|all}}}}})<noinclude> {{Documentation}} </noinclude> f93f1540b8c157703bd6d24ae35c35bef745981d Template:Navbox 10 436 872 2021-06-26T18:05:09Z wikipedia>Trialpears 0 Remove TfD notice as it wouldn't involve any changes to this template. wikitext text/x-wiki <includeonly>{{#invoke:Navbox|navbox}}</includeonly><noinclude> {{Documentation}} </noinclude> fe9b964401f895918ee4fe078678f1722a3c41ec Module:Pagetype/config 828 407 814 2021-07-10T15:47:32Z wikipedia>Trialpears 0 Book namespace removal will happen within a few days Scribunto text/plain -------------------------------------------------------------------------------- -- Module:Pagetype configuration data -- -- This page holds localisation and configuration data for Module:Pagetype. -- -------------------------------------------------------------------------------- local cfg = {} -- Don't edit this line. -------------------------------------------------------------------------------- -- Start configuration data -- -------------------------------------------------------------------------------- -- This table holds the values to use for "main=true", "user=true", etc. Keys to -- this table should be namespace parameters that can be used with -- [[Module:Namespace detect]]. cfg.pagetypes = { ['main'] = 'article', ['user'] = 'user page', ['project'] = 'project page', ['wikipedia'] = 'project page', ['wp'] = 'project page', ['file'] = 'file', ['image'] = 'file', ['mediawiki'] = 'interface page', ['template'] = 'template', ['help'] = 'help page', ['category'] = 'category', ['portal'] = 'portal', ['draft'] = 'draft', ['timedtext'] = 'Timed Text page', ['module'] = 'module', ['topic'] = 'topic', ['gadget'] = 'gadget', ['gadget definition'] = 'gadget definition', ['talk'] = 'talk page', ['special'] = 'special page', ['media'] = 'file', } -- This table holds the names of the namespaces to be looked up from -- cfg.pagetypes by default. cfg.defaultNamespaces = { 'main', 'file', 'template', 'category', 'module' } -- This table holds the names of the namespaces to be looked up from -- cfg.pagetypes if cfg.defaultnsExtended is set. cfg.extendedNamespaces = { 'main', 'user', 'project', 'file', 'mediawiki', 'template', 'category', 'help', 'portal', 'module', 'draft' } -- The parameter name to set which default namespace values to be looked up from -- cfg.pagetypes. cfg.defaultns = 'defaultns' -- The value of cfg.defaultns to set all namespaces, including talk. cfg.defaultnsAll = 'all' -- The value of cfg.defaultns to set the namespaces listed in -- cfg.extendedNamespaces cfg.defaultnsExtended = 'extended' -- The value of cfg.defaultns to set no default namespaces. cfg.defaultnsNone = 'none' -- The parameter name to use for disambiguation pages page. cfg.dab = 'dab' -- This table holds the different possible aliases for disambiguation-class -- pages. These should be lower-case. cfg.dabAliases = { 'disambiguation', 'disambig', 'disamb', 'dab' } -- The default value for disambiguation pages. cfg.dabDefault = 'page' -- The parameter name to use for N/A-class page. cfg.na = 'na' -- This table holds the different possible aliases for N/A-class pages. These -- should be lower-case. cfg.naAliases = {'na', 'n/a'} -- The default value for N/A-class pages. cfg.naDefault = 'page' -- The parameter name to use for redirects. cfg.redirect = 'redirect' -- The default value to use for redirects. cfg.redirectDefault = 'redirect' -- The parameter name for undefined namespaces. cfg.other = 'other' -- The value used if the module detects an undefined namespace. cfg.otherDefault = 'page' -- The usual suffix denoting a plural. cfg.plural = 's' -- This table holds plurals not formed by a simple suffix. cfg.irregularPlurals = { ["category"] = "categories" } -------------------------------------------------------------------------------- -- End configuration data -- -------------------------------------------------------------------------------- return cfg -- Don't edit this line e2eb36d6c43611a422bae37947ebeb04b695dcba Template:Hatnote/styles.css 10 318 631 2021-07-12T19:22:27Z wikipedia>Izno 0 per my talk page text text/plain /* {{pp|small=y}} */ .hatnote { font-style: italic; } /* Limit structure CSS to divs because of [[Module:Hatnote inline]] */ div.hatnote { /* @noflip */ padding-left: 1.6em; margin-bottom: 0.5em; } .hatnote i { font-style: normal; } /* The templatestyles element inserts a link element before hatnotes. * TODO: Remove link if/when WMF resolves T200206 */ .hatnote + link + .hatnote { margin-top: -0.5em; } 44680ffd6e888866df2cdfa0341af9c7b97da94c Module:Hatnote/styles.css 828 487 972 2021-07-12T19:22:27Z infoboxes>Izno 0 per my talk page text text/plain /* {{pp|small=y}} */ .hatnote { font-style: italic; } /* Limit structure CSS to divs because of [[Module:Hatnote inline]] */ div.hatnote { /* @noflip */ padding-left: 1.6em; margin-bottom: 0.5em; } .hatnote i { font-style: normal; } /* The templatestyles element inserts a link element before hatnotes. * TODO: Remove link if/when WMF resolves T200206 */ .hatnote + link + .hatnote { margin-top: -0.5em; } 44680ffd6e888866df2cdfa0341af9c7b97da94c Template:If both 10 405 810 2021-07-27T21:26:23Z wikipedia>Trialpears 0 substitutable wikitext text/x-wiki {{{{{|safesubst:}}}#if:{{{1|}}}| {{{{{|safesubst:}}}#if:{{{2|}}}|{{{3|}}}|{{{4|}}}}} |{{{4|}}} }}<noinclude> {{Documentation}} <!-- PLEASE ADD CATEGORIES AND INTERWIKIS TO THE /doc SUBPAGE, THANKS --> </noinclude> d77fc191cada8977a8131dd6d85dde5e31d0e6f2 Template:Template parameter usage 10 334 663 2021-08-21T18:02:56Z wikipedia>SUM1 0 Added missing "lc" parameters; added optional "based" parameter to add text "based on this[/its] TemplateData" at end of template wikitext text/x-wiki {{#switch:{{{label|}}} |=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{#ifeq:{{yesno-no|{{{lc}}}}}|no|C|c}}lick here] to see a monthly parameter usage report for {{#if:{{{1|}}}|[[Template:{{ROOTPAGENAME:{{{1|}}}}}]]|this template}}{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}}. |None|none=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{#ifeq:{{yesno-no|{{{lc}}}}}|no|P|p}}arameter usage report]{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}} |for|For=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{#ifeq:{{yesno-no|{{{lc}}}}}|no|P|p}}arameter usage report] for {{#if:{{{1|}}}|[[Template:{{ROOTPAGENAME:{{{1|}}}}}]]|[[Template:{{ROOTPAGENAME}}]]}}{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}}. |#default=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{{label|}}}]{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}} }}<noinclude> {{documentation}} </noinclude> b9cdd1b2e409313904f041c38562a3d6221cc017 Template:TemplateData header 10 333 661 2021-08-29T21:32:29Z wikipedia>SUM1 0 Added "based" parameter to other transclusion wikitext text/x-wiki <div class="templatedata-header">{{#if:{{{noheader|}}}|<!-- noheader: -->{{Template parameter usage|based=y}}|<!-- +header: -->This is the {{#if:{{{nolink|}}}|<!-- +header, nolink TD -->TemplateData|<!-- +header, +link [[TD]]; DEFAULT: -->[[Wikipedia:TemplateData|TemplateData]]}}<!-- e.o. #if:nolink; DEFAULT: --> for this template used by [[mw:Extension:TemplateWizard|TemplateWizard]], [[Wikipedia:VisualEditor|VisualEditor]] and other tools. {{Template parameter usage|based=y}}<!-- e.o. #if:noheader -->}} '''TemplateData for {{{1|{{BASEPAGENAME}}}}}''' </div><includeonly><!-- check parameters -->{{#invoke:Check for unknown parameters|check |unknown={{template other|1=[[Category:Pages using TemplateData header with unknown parameters|_VALUE_]]}} |template=Template:TemplateData header |1 |nolink |noheader |preview=<div class="error" style="font-weight:normal">Unknown parameter '_VALUE_' in [[Template:TemplateData header]].</div> }}<!-- -->{{template other|{{sandbox other|| [[Category:Templates using TemplateData]] }}}}</includeonly><!-- --><noinclude>{{Documentation}}</noinclude> ddfbb4ae793846b96d4c06330417fa6ed4da2adc Module:Pf 828 522 1052 2021-09-19T20:47:25Z string2>Primefac 0 Primefac moved page [[Template:Pf]] to [[Template:ParserFunction]]: expand name for clarity wikitext text/x-wiki #REDIRECT [[Template:ParserFunction]] {{Redirect category shell| {{R from move}} }} 189e6267904ed6300f671171532db2aa0767ee3a Template:Sidebar/styles.css 10 320 635 2021-09-20T01:15:45Z wikipedia>Goszei 0 self-rv, it's a little tight text text/plain /* {{pp-template}} */ /* TODO: Invert width design to be "mobile first" */ .sidebar { /* TODO: Ask if we should have max-width 22em instead */ width: 22em; /* @noflip */ float: right; /* @noflip */ clear: right; /* @noflip */ margin: 0.5em 0 1em 1em; background: #f8f9fa; border: 1px solid #aaa; padding: 0.2em; text-align: center; line-height: 1.4em; font-size: 88%; border-collapse: collapse; /* Timeless has display: none on .nomobile at mobile resolutions, so we * unhide it with display: table and let precedence and proximity win. */ display: table; } /* Unfortunately, so does Minerva desktop, except Minerva drops an * !important on the declaration. So we have to be mean for Minerva users. * Mobile removes the element entirely with `wgMFRemovableClasses` in * https://github.com/wikimedia/operations-mediawiki-config/blob/master/ wmf-config/InitialiseSettings.php#L16992 * which is why displaying it categorically with display: table works. * We don't really want to expose the generic user in the wild on mobile to have * to deal with sidebars. (Maybe the ones with collapsible lists, so that * might be an improvement. That is blocked on [[:phab:T111565]].) */ body.skin-minerva .sidebar { display: table !important; /* also, minerva is way too aggressive about other stylings on tables. * TODO remove when this template gets moved to a div. plans on talk page. * We always float right on Minerva because that's a lot of extra CSS * otherwise. */ float: right !important; margin: 0.5em 0 1em 1em !important; } .sidebar-subgroup { width: 100%; margin: 0; border-spacing: 0; } .sidebar-left { /* @noflip */ float: left; /* @noflip */ clear: left; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-none { float: none; clear: both; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-outer-title { padding: 0 0.4em 0.2em; font-size: 125%; line-height: 1.2em; font-weight: bold; } .sidebar-top-image { padding: 0.4em; } .sidebar-top-caption, .sidebar-pretitle-with-top-image, .sidebar-caption { padding: 0.2em 0.4em 0; line-height: 1.2em; } .sidebar-pretitle { padding: 0.4em 0.4em 0; line-height: 1.2em; } .sidebar-title, .sidebar-title-with-pretitle { padding: 0.2em 0.8em; font-size: 145%; line-height: 1.2em; } .sidebar-title-with-pretitle { padding: 0.1em 0.4em; } .sidebar-image { padding: 0.2em 0.4em 0.4em; } .sidebar-heading { padding: 0.1em 0.4em; } .sidebar-content { padding: 0 0.5em 0.4em; } .sidebar-content-with-subgroup { padding: 0.1em 0.4em 0.2em; } .sidebar-above, .sidebar-below { padding: 0.3em 0.8em; font-weight: bold; } .sidebar-collapse .sidebar-above, .sidebar-collapse .sidebar-below { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; } .sidebar-navbar { text-align: right; font-size: 115%; padding: 0 0.4em 0.4em; } .sidebar-list-title { padding: 0 0.4em; text-align: left; font-weight: bold; line-height: 1.6em; font-size: 105%; } /* centered text with mw-collapsible headers is finicky */ .sidebar-list-title-c { padding: 0 0.4em; text-align: center; margin: 0 3.3em; } @media (max-width: 720px) { /* users have wide latitude to set arbitrary width and margin :( "Super-specific" selector to prevent overriding this appearance by lower level sidebars too */ body.mediawiki .sidebar { width: 100% !important; clear: both; float: none !important; /* Remove when we div based; Minerva is dumb */ margin-left: 0 !important; margin-right: 0 !important; } /* TODO: We might consider making all links wrap at small resolutions and then * only introduce nowrap at higher resolutions. Do when we invert the media * query. */ } 7d621b35a37807a103b59075851fe36201204ceb Module:Sidebar/styles.css 828 491 980 2021-09-20T01:15:45Z infoboxes>Goszei 0 self-rv, it's a little tight text text/plain /* {{pp-template}} */ /* TODO: Invert width design to be "mobile first" */ .sidebar { /* TODO: Ask if we should have max-width 22em instead */ width: 22em; /* @noflip */ float: right; /* @noflip */ clear: right; /* @noflip */ margin: 0.5em 0 1em 1em; background: #f8f9fa; border: 1px solid #aaa; padding: 0.2em; text-align: center; line-height: 1.4em; font-size: 88%; border-collapse: collapse; /* Timeless has display: none on .nomobile at mobile resolutions, so we * unhide it with display: table and let precedence and proximity win. */ display: table; } /* Unfortunately, so does Minerva desktop, except Minerva drops an * !important on the declaration. So we have to be mean for Minerva users. * Mobile removes the element entirely with `wgMFRemovableClasses` in * https://github.com/wikimedia/operations-mediawiki-config/blob/master/ wmf-config/InitialiseSettings.php#L16992 * which is why displaying it categorically with display: table works. * We don't really want to expose the generic user in the wild on mobile to have * to deal with sidebars. (Maybe the ones with collapsible lists, so that * might be an improvement. That is blocked on [[:phab:T111565]].) */ body.skin-minerva .sidebar { display: table !important; /* also, minerva is way too aggressive about other stylings on tables. * TODO remove when this template gets moved to a div. plans on talk page. * We always float right on Minerva because that's a lot of extra CSS * otherwise. */ float: right !important; margin: 0.5em 0 1em 1em !important; } .sidebar-subgroup { width: 100%; margin: 0; border-spacing: 0; } .sidebar-left { /* @noflip */ float: left; /* @noflip */ clear: left; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-none { float: none; clear: both; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-outer-title { padding: 0 0.4em 0.2em; font-size: 125%; line-height: 1.2em; font-weight: bold; } .sidebar-top-image { padding: 0.4em; } .sidebar-top-caption, .sidebar-pretitle-with-top-image, .sidebar-caption { padding: 0.2em 0.4em 0; line-height: 1.2em; } .sidebar-pretitle { padding: 0.4em 0.4em 0; line-height: 1.2em; } .sidebar-title, .sidebar-title-with-pretitle { padding: 0.2em 0.8em; font-size: 145%; line-height: 1.2em; } .sidebar-title-with-pretitle { padding: 0.1em 0.4em; } .sidebar-image { padding: 0.2em 0.4em 0.4em; } .sidebar-heading { padding: 0.1em 0.4em; } .sidebar-content { padding: 0 0.5em 0.4em; } .sidebar-content-with-subgroup { padding: 0.1em 0.4em 0.2em; } .sidebar-above, .sidebar-below { padding: 0.3em 0.8em; font-weight: bold; } .sidebar-collapse .sidebar-above, .sidebar-collapse .sidebar-below { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; } .sidebar-navbar { text-align: right; font-size: 115%; padding: 0 0.4em 0.4em; } .sidebar-list-title { padding: 0 0.4em; text-align: left; font-weight: bold; line-height: 1.6em; font-size: 105%; } /* centered text with mw-collapsible headers is finicky */ .sidebar-list-title-c { padding: 0 0.4em; text-align: center; margin: 0 3.3em; } @media (max-width: 720px) { /* users have wide latitude to set arbitrary width and margin :( "Super-specific" selector to prevent overriding this appearance by lower level sidebars too */ body.mediawiki .sidebar { width: 100% !important; clear: both; float: none !important; /* Remove when we div based; Minerva is dumb */ margin-left: 0 !important; margin-right: 0 !important; } /* TODO: We might consider making all links wrap at small resolutions and then * only introduce nowrap at higher resolutions. Do when we invert the media * query. */ } 7d621b35a37807a103b59075851fe36201204ceb Module:GetParameters 828 502 1006 2021-10-07T13:22:34Z string2>Andrybak 0 drop misleading comment copy-pasted from [[Module:String]] Scribunto text/plain local p = {} --[[ Helper function that populates the argument list given that user may need to use a mix of named and unnamed parameters. This is relevant because named parameters are not identical to unnamed parameters due to string trimming, and when dealing with strings we sometimes want to either preserve or remove that whitespace depending on the application. ]] function p.getParameters( frame_args, arg_list ) local new_args = {}; local index = 1; local value; for i,arg in ipairs( arg_list ) do value = frame_args[arg] if value == nil then value = frame_args[index]; index = index + 1; end new_args[arg] = value; end return new_args; end --[[ Helper Function to interpret boolean strings ]] function p.getBoolean( boolean_str ) local boolean_value; if type( boolean_str ) == 'string' then boolean_str = boolean_str:lower(); if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0' or boolean_str == '' then boolean_value = false; else boolean_value = true; end elseif type( boolean_str ) == 'boolean' then boolean_value = boolean_str; else error( 'No boolean value found' ); end return boolean_value end function p.defined(frame) local arg = mw.text.trim(frame.args[1]) --if arg == tostring(tonumber(arg)) then -- undesired result for '-0' -- arg = tonumber(arg) --end --if mw.ustring.find(arg, '^%s*-?[1-9][0-9]*%s*$') ~= nil or arg == '0' then -- arg = tonumber(arg) --end if mw.ustring.find(arg, '^-?[1-9][0-9]*$') ~= nil then arg = tonumber(arg) elseif arg == '0' then arg = 0 end return frame:getParent().args[arg] ~= nil end return p 00e952f0ee8f6ea68e990d589dfb15e7d4036623 Module:Unsubst 828 395 790 2021-10-08T18:22:16Z wikipedia>Trappist the monk 0 sync from sandbox; see [[Module_talk:Unsubst#template_invocation_name_override|talk]]; Scribunto text/plain local checkType = require('libraryUtil').checkType local p = {} local BODY_PARAM = '$B' local specialParams = { ['$params'] = 'parameter list', ['$aliases'] = 'parameter aliases', ['$flags'] = 'flags', ['$B'] = 'template content', ['$template-name'] = 'template invocation name override', } function p.main(frame, body) -- If we are substing, this function returns a template invocation, and if -- not, it returns the template body. The template body can be specified in -- the body parameter, or in the template parameter defined in the -- BODY_PARAM variable. This function can be called from Lua or from -- #invoke. -- Return the template body if we aren't substing. if not mw.isSubsting() then if body ~= nil then return body elseif frame.args[BODY_PARAM] ~= nil then return frame.args[BODY_PARAM] else error(string.format( "no template content specified (use parameter '%s' from #invoke)", BODY_PARAM ), 2) end end -- Sanity check for the frame object. if type(frame) ~= 'table' or type(frame.getParent) ~= 'function' or not frame:getParent() then error( "argument #1 to 'main' must be a frame object with a parent " .. "frame available", 2 ) end -- Find the invocation name. local mTemplateInvocation = require('Module:Template invocation') local name if frame.args['$template-name'] and '' ~= frame.args['$template-name'] then name = frame.args['$template-name'] -- override whatever the template name is with this name else name = mTemplateInvocation.name(frame:getParent():getTitle()) end -- Combine passed args with passed defaults local args = {} if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*override%s*,' ) then for k, v in pairs( frame:getParent().args ) do args[k] = v end for k, v in pairs( frame.args ) do if not specialParams[k] then if v == '__DATE__' then v = mw.getContentLanguage():formatDate( 'F Y' ) end args[k] = v end end else for k, v in pairs( frame.args ) do if not specialParams[k] then if v == '__DATE__' then v = mw.getContentLanguage():formatDate( 'F Y' ) end args[k] = v end end for k, v in pairs( frame:getParent().args ) do args[k] = v end end -- Trim parameters, if not specified otherwise if not string.find( ','..(frame.args['$flags'] or '')..',', ',%s*keep%-whitespace%s*,' ) then for k, v in pairs( args ) do args[k] = mw.ustring.match(v, '^%s*(.*)%s*$') or '' end end -- Pull information from parameter aliases local aliases = {} if frame.args['$aliases'] then local list = mw.text.split( frame.args['$aliases'], '%s*,%s*' ) for k, v in ipairs( list ) do local tmp = mw.text.split( v, '%s*>%s*' ) aliases[tonumber(mw.ustring.match(tmp[1], '^[1-9][0-9]*$')) or tmp[1]] = ((tonumber(mw.ustring.match(tmp[2], '^[1-9][0-9]*$'))) or tmp[2]) end end for k, v in pairs( aliases ) do if args[k] and ( not args[v] or args[v] == '' ) then args[v] = args[k] end args[k] = nil end -- Remove empty parameters, if specified if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*remove%-empty%s*,' ) then local tmp = 0 for k, v in ipairs( args ) do if v ~= '' or ( args[k+1] and args[k+1] ~= '' ) or ( args[k+2] and args[k+2] ~= '' ) then tmp = k else break end end for k, v in pairs( args ) do if v == '' then if not (type(k) == 'number' and k < tmp) then args[k] = nil end end end end -- Order parameters if frame.args['$params'] then local params, tmp = mw.text.split( frame.args['$params'], '%s*,%s*' ), {} for k, v in ipairs(params) do v = tonumber(mw.ustring.match(v, '^[1-9][0-9]*$')) or v if args[v] then tmp[v], args[v] = args[v], nil end end for k, v in pairs(args) do tmp[k], args[k] = args[k], nil end args = tmp end return mTemplateInvocation.invocation(name, args) end p[''] = p.main -- For backwards compatibility return p 7f01ffc8aa2ac4a4772f14c12e0b77e384ecabb6 Module:Module other 828 368 730 2021-10-20T19:50:22Z wikipedia>MusikBot II 0 Changed protection settings for "[[Template:Module other]]": [[Wikipedia:High-risk templates|High-risk template or module]]: 3570 transclusions ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite)) wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:Module}} | module | other }} }} | module = {{{1|}}} | other | #default = {{{2|}}} }}<!--End switch--><noinclude> {{documentation}} <!-- Add categories to the /doc subpage, not here! --> </noinclude> 503694836c1b07142e63fd35d8be69ec8bb9ffe7 Template:Module other 10 378 752 2021-10-20T19:50:22Z wikipedia>MusikBot II 0 Changed protection settings for "[[Template:Module other]]": [[Wikipedia:High-risk templates|High-risk template or module]]: 3570 transclusions ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite)) wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:Module}} | module | other }} }} | module = {{{1|}}} | other | #default = {{{2|}}} }}<!--End switch--><noinclude> {{documentation}} <!-- Add categories to the /doc subpage, not here! --> </noinclude> 503694836c1b07142e63fd35d8be69ec8bb9ffe7 Template:Italic title 10 452 904 2021-10-22T16:08:51Z wikipedia>Gonnym 0 includeonly wikitext text/x-wiki <includeonly>{{#invoke:Italic title|main}}</includeonly><noinclude> {{Documentation}} <!-- Add categories to the /doc subpage and interwikis to Wikidata. --> </noinclude> aa77d0fbc6bb86266d633b290b7ab258cc69c8b8 Template:Ambox 10 394 788 2021-10-28T18:27:47Z wikipedia>GKFX 0 Copy fix from sandbox; only articles should go in this category. wikitext text/x-wiki {{#invoke:Message box|ambox}}{{#ifeq:{{{small}}};{{NAMESPACENUMBER}}|left;0|[[Category:Articles using small message boxes]]}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 0c13ec156138ae0499c998cc3d7fbbeac4aeeed6 Module:Boston Bridge 828 527 1062 2021-11-14T19:36:01Z string2>Mitchazenia 0 missed that one wikitext text/x-wiki {{Infobox Bridge |bridge_name = Boston Bridge |image = Boston Bridge 2014.jpg |image_size = 300px |caption =Boston Bridge in September&nbsp;2014. |carries= {{jct|state=PA|PA|48}} |crosses= [[Youghiogheny River]] |locale= [[Versailles, Pennsylvania|Versailles]] and [[Elizabeth Township, Allegheny County, Pennsylvania|Elizabeth Township]], [[Pennsylvania]] |maint= |id= |design= [[cantilever bridge]] |mainspan= |length= 1181 f |width= 30 ft |below= |traffic= |open= October&nbsp;13, 1932<ref>{{cite news |title=New Boston Bridge Opened to Traffic |url=https://www.newspapers.com/clip/29570035/the-pittsburgh-press/ |access-date=November 12, 2021 |work=[[The Pittsburgh Press]] |date=October 14, 1932 |page=4|via=Newspapers.com}} {{open access}}</ref> |closed= |toll= |map_cue= |map_image= |map_text= |map_width= |lat= |long= }} The '''Boston Bridge''' is a structure that crosses the [[Youghiogheny River]] between [[Versailles, Pennsylvania|Versailles]] and [[Elizabeth Township, Allegheny County, Pennsylvania|Elizabeth Township]], [[Pennsylvania]]. Its name is derived from the Massachusetts city only indirectly: the bridge is named for the Boston neighborhood of Elizabeth Township, which in turn was named for the New England city. The bridge, which opened on October&nbsp;13, 1932, carries [[Pennsylvania Route 48]] on two relatively narrow lanes. Many of its features were carefully preserved during a 1989 rehabilitation, down to its original pedestrian railings. Designed entirely for vehicular traffic, the structure changed the future of Elizabeth Township; the lack of streetcar tracks led to the abandonment of a line that served the then-rural community's small industrial settlements, while the newfound ease of access for motorists to the area's manufacturing regions opened up the township to [[suburbanization]]. ==References== {{Reflist}} == External links == {{commonscat}} *[http://www.pghbridges.com/mckeesport/0599-4462/boston.htm PGH Bridges] {{Crossings navbox |structure = Bridges |place = [[Youghiogheny River]] |bridge = Boston Bridge |bridge signs = [[Image:PA-48.svg|20px]] [[Pennsylvania Route 48|PA 48]] |downstream = [[P&LE Liberty Boro Bridge]] |downstream signs = [[CSX]] [[Keystone Subdivision]] |upstream = [[Sutersville Bridge]] |upstream signs = }} {{coord|40.3128|-79.8283|region:US-PA_type:landmark|display=title}} [[Category:Bridges in Allegheny County, Pennsylvania]] [[Category:Bridges completed in 1931]] [[Category:Road bridges in Pennsylvania]] [[Category:Bridges over the Youghiogheny River]] [[Category:Cantilever bridges in the United States]] [[Category:1931 establishments in Pennsylvania]] f35d6669ef86b5e078f2288373933e86dd462b5a Template:Main other 10 308 611 2021-12-10T16:08:06Z wikipedia>Xaosflux 0 <!-- Add categories to the /doc subpage; interwikis go to Wikidata, thank you! --> wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:0}} | main | other }} }} | main = {{{1|}}} | other | #default = {{{2|}}} }}<noinclude> {{documentation}} <!-- Add categories to the /doc subpage; interwikis go to Wikidata, thank you! --> </noinclude> 86ad907ffeea3cc545159e00cd1f2d6433946450 Module:Navbox/styles.css 828 445 890 2021-12-21T22:10:10Z wikipedia>Izno 0 remove qualifications on th - this will remove styling from "hand-crafted" navboxes, but there's no other elegant way to deal with it. see talk page text text/plain /* {{pp|small=y}} */ .navbox { box-sizing: border-box; border: 1px solid #a2a9b1; width: 100%; clear: both; font-size: 88%; text-align: center; padding: 1px; margin: 1em auto 0; /* Prevent preceding content from clinging to navboxes */ } .navbox .navbox { margin-top: 0; /* No top margin for nested navboxes */ } .navbox + .navbox, /* TODO: remove first line after transclusions have updated */ .navbox + .navbox-styles + .navbox { margin-top: -1px; /* Single pixel border between adjacent navboxes */ } .navbox-inner, .navbox-subgroup { width: 100%; } .navbox-group, .navbox-title, .navbox-abovebelow { padding: 0.25em 1em; line-height: 1.5em; text-align: center; } .navbox-group { white-space: nowrap; /* @noflip */ text-align: right; } .navbox, .navbox-subgroup { background-color: #fdfdfd; } .navbox-list { line-height: 1.5em; border-color: #fdfdfd; /* Must match background color */ } .navbox-list-with-group { text-align: left; border-left-width: 2px; border-left-style: solid; } /* cell spacing for navbox cells */ /* Borders above 2nd, 3rd, etc. rows */ /* TODO: figure out how to replace tr as structure; * with div structure it should be just a matter of first-child */ tr + tr > .navbox-abovebelow, tr + tr > .navbox-group, tr + tr > .navbox-image, tr + tr > .navbox-list { border-top: 2px solid #fdfdfd; /* Must match background color */ } .navbox-title { background-color: #ccf; /* Level 1 color */ } .navbox-abovebelow, .navbox-group, .navbox-subgroup .navbox-title { background-color: #ddf; /* Level 2 color */ } .navbox-subgroup .navbox-group, .navbox-subgroup .navbox-abovebelow { background-color: #e6e6ff; /* Level 3 color */ } .navbox-even { background-color: #f7f7f7; } .navbox-odd { background-color: transparent; } /* TODO: figure out how to remove reliance on td as structure */ .navbox .hlist td dl, .navbox .hlist td ol, .navbox .hlist td ul, .navbox td.hlist dl, .navbox td.hlist ol, .navbox td.hlist ul { padding: 0.125em 0; } .navbox .navbar { display: block; font-size: 100%; } .navbox-title .navbar { /* @noflip */ float: left; /* @noflip */ text-align: left; /* @noflip */ margin-right: 0.5em; } e80b0d7a5770e6e105dab832deb6c37a5245ebc6 Template:Notelist 10 451 902 2021-12-25T05:48:43Z wikipedia>Jonesey95 0 Adding unknown parameter tracking through [[:Category:Pages using notelist with unknown parameters]] using [[Module:check for unknown parameters]]. How was this not here before? wikitext text/x-wiki {{reflist|{{{1|{{{colwidth|}}}}}}|refs={{{refs|{{{notes|}}}}}}|group={{#switch: {{{group|}}} | note | upper-alpha | upper-roman | lower-alpha | lower-greek | lower-roman = {{{group|}}} | #default = lower-alpha }}}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using notelist with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Notelist]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | colwidth | group | notes | refs }}<noinclude> {{documentation}}</noinclude> 093e937792d01a5dfc9dd3b5e5deda6b48d0f2be Module:Hatnote list 828 488 974 2021-12-26T20:05:24Z infoboxes>Nihiltres 0 Undid revision 1062166786 by [[Special:Contributions/Matthiaspaul|Matthiaspaul]] ([[User talk:Matthiaspaul|talk]]); should be fixed now, and if not, please ping me with examples as I couldn't reproduce the original error Scribunto text/plain -------------------------------------------------------------------------------- -- Module:Hatnote list -- -- -- -- This module produces and formats lists for use in hatnotes. In particular, -- -- it implements the for-see list, i.e. lists of "For X, see Y" statements, -- -- as used in {{about}}, {{redirect}}, and their variants. Also introduced -- -- are andList & orList helpers for formatting lists with those conjunctions. -- -------------------------------------------------------------------------------- local mArguments --initialize lazily local mFormatLink = require('Module:Format link') local mHatnote = require('Module:Hatnote') local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local p = {} -------------------------------------------------------------------------------- -- List stringification helper functions -- -- These functions are used for stringifying lists, usually page lists inside -- the "Y" portion of "For X, see Y" for-see items. -------------------------------------------------------------------------------- --default options table used across the list stringification functions local stringifyListDefaultOptions = { conjunction = "and", separator = ",", altSeparator = ";", space = " ", formatted = false } --Searches display text only local function searchDisp(haystack, needle) return string.find( string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle ) end -- Stringifies a list generically; probably shouldn't be used directly local function stringifyList(list, options) -- Type-checks, defaults, and a shortcut checkType("stringifyList", 1, list, "table") if #list == 0 then return nil end checkType("stringifyList", 2, options, "table", true) options = options or {} for k, v in pairs(stringifyListDefaultOptions) do if options[k] == nil then options[k] = v end end local s = options.space -- Format the list if requested if options.formatted then list = mFormatLink.formatPages( {categorizeMissing = mHatnote.missingTargetCat}, list ) end -- Set the separator; if any item contains it, use the alternate separator local separator = options.separator for k, v in pairs(list) do if searchDisp(v, separator) then separator = options.altSeparator break end end -- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§" local conjunction = s .. options.conjunction .. s if #list == 2 and searchDisp(list[1], "§") or #list > 2 then conjunction = separator .. conjunction end -- Return the formatted string return mw.text.listToText(list, separator .. s, conjunction) end --DRY function function p.conjList (conj, list, fmt) return stringifyList(list, {conjunction = conj, formatted = fmt}) end -- Stringifies lists with "and" or "or" function p.andList (...) return p.conjList("and", ...) end function p.orList (...) return p.conjList("or", ...) end -------------------------------------------------------------------------------- -- For see -- -- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the -- {{about}} and {{redirect}} templates and their variants. -------------------------------------------------------------------------------- --default options table used across the forSee family of functions local forSeeDefaultOptions = { andKeyword = 'and', title = mw.title.getCurrentTitle().text, otherText = 'other uses', forSeeForm = 'For %s, see %s.', } --Collapses duplicate punctuation local function punctuationCollapse (text) local replacements = { ["%.%.$"] = ".", ["%?%.$"] = "?", ["%!%.$"] = "!", ["%.%]%]%.$"] = ".]]", ["%?%]%]%.$"] = "?]]", ["%!%]%]%.$"] = "!]]" } for k, v in pairs(replacements) do text = string.gsub(text, k, v) end return text end -- Structures arguments into a table for stringification, & options function p.forSeeArgsToTable (args, from, options) -- Type-checks and defaults checkType("forSeeArgsToTable", 1, args, 'table') checkType("forSeeArgsToTable", 2, from, 'number', true) from = from or 1 checkType("forSeeArgsToTable", 3, options, 'table', true) options = options or {} for k, v in pairs(forSeeDefaultOptions) do if options[k] == nil then options[k] = v end end -- maxArg's gotten manually because getArgs() and table.maxn aren't friends local maxArg = 0 for k, v in pairs(args) do if type(k) == 'number' and k > maxArg then maxArg = k end end -- Structure the data out from the parameter list: -- * forTable is the wrapper table, with forRow rows -- * Rows are tables of a "use" string & a "pages" table of pagename strings -- * Blanks are left empty for defaulting elsewhere, but can terminate list local forTable = {} local i = from local terminated = false -- If there is extra text, and no arguments are given, give nil value -- to not produce default of "For other uses, see foo (disambiguation)" if options.extratext and i > maxArg then return nil end -- Loop to generate rows repeat -- New empty row local forRow = {} -- On blank use, assume list's ended & break at end of this loop forRow.use = args[i] if not args[i] then terminated = true end -- New empty list of pages forRow.pages = {} -- Insert first pages item if present table.insert(forRow.pages, args[i + 1]) -- If the param after next is "and", do inner loop to collect params -- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3} while args[i + 2] == options.andKeyword do if args[i + 3] then table.insert(forRow.pages, args[i + 3]) end -- Increment to next "and" i = i + 2 end -- Increment to next use i = i + 2 -- Append the row table.insert(forTable, forRow) until terminated or i > maxArg return forTable end -- Stringifies a table as formatted by forSeeArgsToTable function p.forSeeTableToString (forSeeTable, options) -- Type-checks and defaults checkType("forSeeTableToString", 1, forSeeTable, "table", true) checkType("forSeeTableToString", 2, options, "table", true) options = options or {} for k, v in pairs(forSeeDefaultOptions) do if options[k] == nil then options[k] = v end end -- Stringify each for-see item into a list local strList = {} if forSeeTable then for k, v in pairs(forSeeTable) do local useStr = v.use or options.otherText local pagesStr = p.andList(v.pages, true) or mFormatLink._formatLink{ categorizeMissing = mHatnote.missingTargetCat, link = mHatnote.disambiguate(options.title) } local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr) forSeeStr = punctuationCollapse(forSeeStr) table.insert(strList, forSeeStr) end end if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end -- Return the concatenated list return table.concat(strList, ' ') end -- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps -- but not blank/whitespace values. Ignores named args and args < "from". function p._forSee (args, from, options) local forSeeTable = p.forSeeArgsToTable(args, from, options) return p.forSeeTableToString(forSeeTable, options) end -- As _forSee, but uses the frame. function p.forSee (frame, from, options) mArguments = require('Module:Arguments') return p._forSee(mArguments.getArgs(frame), from, options) end return p d0828422b1aa0d0d0092d699d059c9e882260398 Template:Navbar/styles.css 10 319 633 2022-01-03T23:12:15Z wikipedia>Izno 0 navbar styles that were moved to parent templates text text/plain /* {{pp|small=yes}} */ .navbar { display: inline; font-size: 88%; font-weight: normal; } .navbar-collapse { float: left; text-align: left; } .navbar-boxtext { word-spacing: 0; } .navbar ul { display: inline-block; white-space: nowrap; line-height: inherit; } .navbar-brackets::before { margin-right: -0.125em; content: '[ '; } .navbar-brackets::after { margin-left: -0.125em; content: ' ]'; } .navbar li { word-spacing: -0.125em; } .navbar a > span, .navbar a > abbr { text-decoration: inherit; } .navbar-mini abbr { font-variant: small-caps; border-bottom: none; text-decoration: none; cursor: inherit; } .navbar-ct-full { font-size: 114%; margin: 0 7em; } .navbar-ct-mini { font-size: 114%; margin: 0 4em; } 9d4056f949b4f0b159e3d40dfb1a5f01e72f9571 Module:Navbar/styles.css 828 442 884 2022-01-03T23:12:15Z wikipedia>Izno 0 navbar styles that were moved to parent templates text text/plain /* {{pp|small=yes}} */ .navbar { display: inline; font-size: 88%; font-weight: normal; } .navbar-collapse { float: left; text-align: left; } .navbar-boxtext { word-spacing: 0; } .navbar ul { display: inline-block; white-space: nowrap; line-height: inherit; } .navbar-brackets::before { margin-right: -0.125em; content: '[ '; } .navbar-brackets::after { margin-left: -0.125em; content: ' ]'; } .navbar li { word-spacing: -0.125em; } .navbar a > span, .navbar a > abbr { text-decoration: inherit; } .navbar-mini abbr { font-variant: small-caps; border-bottom: none; text-decoration: none; cursor: inherit; } .navbar-ct-full { font-size: 114%; margin: 0 7em; } .navbar-ct-mini { font-size: 114%; margin: 0 4em; } 9d4056f949b4f0b159e3d40dfb1a5f01e72f9571 Template:Rating 10 467 934 2022-01-14T22:33:05Z wikipedia>Jonesey95 0 add missing empty alt= wikitext text/x-wiki {{#if:{{{1|}}} |<span role="img" style="white-space:nowrap" title="{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}">{{#ifexpr:{{{1}}}>=1 |[[File:{{{full|Star full.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}|alt=]]{{Loop |{{#expr:floor{{{1}}}-1}} |[[File:{{{full|Star full.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|alt=]] }} |{{#ifexpr:{{{1}}}>0 |[[File:{{{half|Star half.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}|alt=]] |[[File:{{{empty|Star empty.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}|alt=]] }} }}{{#ifexpr:{{{1}}}>1and{{{1}}}<>ceil{{{1}}} |[[File:{{{half|Star half.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|alt=]] }}{{#ifexpr:{{{2|-1}}}>ceil{{{1}}} |{{Loop |{{#expr:{{{2}}}-ceil{{{1}}}{{#ifexpr:{{{1}}}=0|-1}}}} |[[File:{{{empty|Star empty.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|alt=]] }} }}</span>{{#ifexpr:{{{1}}}-floor{{{1}}}<>0and{{{1}}}-floor{{{1}}}<>.5 |{{main other|[[Category:Pages with incorrect use of Rating template]]}} }} |<span class="error">Please specify a '''rating'''.</span>{{main other|[[Category:Pages with incorrect use of Rating template]]}} }}<noinclude> {{documentation}} </noinclude> 5298598d04bb7ff507691d8d8d08034af42c87f1 Template:Infobox/styles.css 10 311 617 2022-01-18T15:18:00Z wikipedia>Jdlrobson 0 Fix [[phab:T281642]], a pet peeve of mine. This copies across styles from [[MediaWiki:Minerva.css]] text text/plain /* {{pp|small=y}} */ /* * This TemplateStyles sheet deliberately does NOT include the full set of * infobox styles. We are still working to migrate all of the manual * infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]] * DO NOT ADD THEM HERE */ /* * not strictly certain these styles are necessary since the modules now * exclusively output infobox-subbox or infobox, not both * just replicating the module faithfully */ .infobox-subbox { padding: 0; border: none; margin: -3px; width: auto; min-width: 100%; font-size: 100%; clear: none; float: none; background-color: transparent; } .infobox-3cols-child { margin: auto; } .infobox .navbar { font-size: 100%; } /* T281642 */ body.skin-minerva .infobox-header, body.skin-minerva .infobox-subheader, body.skin-minerva .infobox-above, body.skin-minerva .infobox-title, body.skin-minerva .infobox-image, body.skin-minerva .infobox-full-data, body.skin-minerva .infobox-below { text-align: center; } e8de6d96f4fde53afc4a6b0fed534405ab59b0a7 Module:Citation/CS1/Utilities 828 399 798 2022-01-22T14:11:16Z wikipedia>Trappist the monk 0 update per [[Wikipedia:Village_pump_(proposals)#rfc:_shall_we_update_cs1/2?|RfC]]; Scribunto text/plain local z = { error_cats_t = {}; -- for categorizing citations that contain errors error_ids_t = {}; -- list of error identifiers; used to prevent duplication of certain errors; local to this module error_msgs_t = {}; -- sequence table of error messages maint_cats_t = {}; -- for categorizing citations that aren't erroneous per se, but could use a little work prop_cats_t = {}; -- for categorizing citations based on certain properties, language of source for instance prop_keys_t = {}; -- for adding classes to the citation's <cite> tag }; --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration --[[--------------------------< I S _ S E T >------------------------------------------------------------------ Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string. ]] local function is_set (var) return not (var == nil or var == ''); end --[[--------------------------< I N _ A R R A Y >-------------------------------------------------------------- Whether needle is in haystack ]] local function in_array (needle, haystack) if needle == nil then return false; end for n, v in ipairs (haystack) do if v == needle then return n; end end return false; end --[[--------------------------< H A S _ A C C E P T _ A S _ W R I T T E N >------------------------------------ When <str> is wholly wrapped in accept-as-written markup, return <str> without markup and true; return <str> and false else with allow_empty = false, <str> must have at least one character inside the markup with allow_empty = true, <str> the markup frame can be empty like (()) to distinguish an empty template parameter from the specific condition "has no applicable value" in citation-context. After further evaluation the two cases might be merged at a later stage, but should be kept separated for now. ]] local function has_accept_as_written (str, allow_empty) if not is_set (str) then return str, false; end local count; if true == allow_empty then str, count = str:gsub ('^%(%((.*)%)%)$', '%1'); -- allows (()) to be an empty set else str, count = str:gsub ('^%(%((.+)%)%)$', '%1'); end return str, 0 ~= count; end --[[--------------------------< S U B S T I T U T E >---------------------------------------------------------- Populates numbered arguments in a message string using an argument table. <args> may be a single string or a sequence table of multiple strings. ]] local function substitute (msg, args) return args and mw.message.newRawMessage (msg, args):plain() or msg; end --[[--------------------------< E R R O R _ C O M M E N T >---------------------------------------------------- Wraps error messages with CSS markup according to the state of hidden. <content> may be a single string or a sequence table of multiple strings. ]] local function error_comment (content, hidden) return substitute (hidden and cfg.presentation['hidden-error'] or cfg.presentation['visible-error'], content); end --[[--------------------------< H Y P H E N _ T O _ D A S H >-------------------------------------------------- Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are returned unmodified. These forms are modified: letter - letter (A - B) digit - digit (4-5) digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d) any other forms are returned unmodified. str may be a comma- or semicolon-separated list ]] local function hyphen_to_dash (str) if not is_set (str) then return str; end local accept; -- boolean str = str:gsub ("(%(%(.-%)%))", function(m) return m:gsub(",", ","):gsub(";", ";") end) -- replace commas and semicolons in accept-as-written markup with similar unicode characters so they'll be ignored during the split str = str:gsub ('&[nm]dash;', {['&ndash;'] = '–', ['&mdash;'] = '—'}); -- replace &mdash; and &ndash; entities with their characters; semicolon mucks up the text.split str = str:gsub ('&#45;', '-'); -- replace HTML numeric entity with hyphen character str = str:gsub ('&nbsp;', ' '); -- replace &nbsp; entity with generic keyboard space character local out = {}; local list = mw.text.split (str, '%s*[,;]%s*'); -- split str at comma or semicolon separators if there are any for _, item in ipairs (list) do -- for each item in the list item, accept = has_accept_as_written (item); -- remove accept-this-as-written markup when it wraps all of item if not accept and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit) item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter) item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or -- digit separator digit hyphen digit separator digit item:match ('^%d+%s*%-%s*%d+$') or -- digit hyphen digit item:match ('^%a+%s*%-%s*%a+$') then -- letter hyphen letter item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2'); -- replace hyphen, remove extraneous space characters else item = mw.ustring.gsub (item, '%s*[–—]%s*', '–'); -- for endash or emdash separated ranges, replace em with en, remove extraneous whitespace end end table.insert (out, item); -- add the (possibly modified) item to the output table end local temp_str = ''; -- concatenate the output table into a comma separated string temp_str, accept = has_accept_as_written (table.concat (out, ', ')); -- remove accept-this-as-written markup when it wraps all of concatenated out if accept then temp_str = has_accept_as_written (str); -- when global markup removed, return original str; do it this way to suppress boolean second return value return temp_str:gsub(",", ","):gsub(";", ";"); else return temp_str:gsub(",", ","):gsub(";", ";"); -- else, return assembled temp_str end end --[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if only link is provided (or link and display are the same), returns a wikilink in the form [[L]]; if neither are provided or link is omitted, returns an empty string. ]=] local function make_wikilink (link, display) if not is_set (link) then return '' end if is_set (display) and link ~= display then return table.concat ({'[[', link, '|', display, ']]'}); else return table.concat ({'[[', link, ']]'}); end end --[[--------------------------< S E T _ M E S S A G E >---------------------------------------------------------- Sets an error message using the ~/Configuration error_conditions{} table along with arguments supplied in the function call, inserts the resulting message in z.error_msgs_t{} sequence table, and returns the error message. <error_id> – key value for appropriate error handler in ~/Configuration error_conditions{} table <arguments> – may be a single string or a sequence table of multiple strings to be subsititued into error_conditions[error_id].message <raw> – boolean true – causes this function to return the error message not wrapped in visible-error, hidden-error span tag; returns error_conditions[error_id].hidden as a second return value does not add message to z.error_msgs_t sequence table false, nil – adds message wrapped in visible-error, hidden-error span tag to z.error_msgs_t returns the error message wrapped in visible-error, hidden-error span tag; there is no second return value <prefix> – string to be prepended to <message> -- TODO: remove support for these unused(?) arguments? <suffix> – string to be appended to <message> TODO: change z.error_cats_t and z.maint_cats_t to have the form cat_name = true? this to avoid dups without having to have an extra table ]] local added_maint_cats = {} -- list of maintenance categories that have been added to z.maint_cats_t; TODO: figure out how to delete this table local function set_message (error_id, arguments, raw, prefix, suffix) local error_state = cfg.error_conditions[error_id]; prefix = prefix or ''; suffix = suffix or ''; if error_state == nil then error (cfg.messages['undefined_error'] .. ': ' .. error_id); -- because missing error handler in Module:Citation/CS1/Configuration elseif is_set (error_state.category) then if error_state.message then -- when error_state.message defined, this is an error message table.insert (z.error_cats_t, error_state.category); else if not added_maint_cats[error_id] then added_maint_cats[error_id] = true; -- note that we've added this category table.insert (z.maint_cats_t, substitute (error_state.category, arguments)); -- make cat name then add to table end return; -- because no message, nothing more to do end end local message = substitute (error_state.message, arguments); message = table.concat ( { message, ' (', make_wikilink ( table.concat ( { cfg.messages['help page link'], '#', error_state.anchor }), cfg.messages['help page label']), ')' }); z.error_ids_t[error_id] = true; if z.error_ids_t['err_citation_missing_title'] and -- if missing-title error already noted in_array (error_id, {'err_bare_url_missing_title', 'err_trans_missing_title'}) then -- and this error is one of these return '', false; -- don't bother because one flavor of missing title is sufficient end message = table.concat ({prefix, message, suffix}); if true == raw then return message, error_state.hidden; -- return message not wrapped in visible-error, hidden-error span tag end message = error_comment (message, error_state.hidden); -- wrap message in visible-error, hidden-error span tag table.insert (z.error_msgs_t, message); -- add it to the messages sequence table return message; -- and done; return value generally not used but is used as a flag in various functions of ~/Identifiers end --[[-------------------------< I S _ A L I A S _ U S E D >----------------------------------------------------- This function is used by select_one() to determine if one of a list of alias parameters is in the argument list provided by the template. Input: args – pointer to the arguments table from calling template alias – one of the list of possible aliases in the aliases lists from Module:Citation/CS1/Configuration index – for enumerated parameters, identifies which one enumerated – true/false flag used to choose how enumerated aliases are examined value – value associated with an alias that has previously been selected; nil if not yet selected selected – the alias that has previously been selected; nil if not yet selected error_list – list of aliases that are duplicates of the alias already selected Returns: value – value associated with alias we selected or that was previously selected or nil if an alias not yet selected selected – the alias we selected or the alias that was previously selected or nil if an alias not yet selected ]] local function is_alias_used (args, alias, index, enumerated, value, selected, error_list) if enumerated then -- is this a test for an enumerated parameters? alias = alias:gsub ('#', index); -- replace '#' with the value in index else alias = alias:gsub ('#', ''); -- remove '#' if it exists end if is_set (args[alias]) then -- alias is in the template's argument list if value ~= nil and selected ~= alias then -- if we have already selected one of the aliases local skip; for _, v in ipairs (error_list) do -- spin through the error list to see if we've added this alias if v == alias then skip = true; break; -- has been added so stop looking end end if not skip then -- has not been added so table.insert (error_list, alias); -- add error alias to the error list end else value = args[alias]; -- not yet selected an alias, so select this one selected = alias; end end return value, selected; -- return newly selected alias, or previously selected alias end --[[--------------------------< A D D _ M A I N T _ C A T >------------------------------------------------------ Adds a category to z.maint_cats_t using names from the configuration file with additional text if any. To prevent duplication, the added_maint_cats table lists the categories by key that have been added to z.maint_cats_t. ]] local function add_maint_cat (key, arguments) if not added_maint_cats [key] then added_maint_cats [key] = true; -- note that we've added this category table.insert (z.maint_cats_t, substitute (cfg.maint_cats [key], arguments)); -- make name then add to table end end --[[--------------------------< A D D _ P R O P _ C A T >-------------------------------------------------------- Adds a category to z.prop_cats_t using names from the configuration file with additional text if any. foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages may be categorized but multiples of the same language are not categorized. added_prop_cats is a table declared in page scope variables above ]] local added_prop_cats = {}; -- list of property categories that have been added to z.prop_cats_t local function add_prop_cat (key, arguments, key_modifier) local key_modified = key .. ((key_modifier and key_modifier) or ''); -- modify <key> with <key_modifier> if present and not nil if not added_prop_cats [key_modified] then added_prop_cats [key_modified] = true; -- note that we've added this category table.insert (z.prop_cats_t, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table table.insert (z.prop_keys_t, 'cs1-prop-' .. key); -- convert key to class for use in the citation's <cite> tag end end --[[--------------------------< S A F E _ F O R _ I T A L I C S >---------------------------------------------- Protects a string that will be wrapped in wiki italic markup '' ... '' Note: We cannot use <i> for italics, as the expected behavior for italics specified by ''...'' in the title is that they will be inverted (i.e. unitalicized) in the resulting references. In addition, <i> and '' tend to interact poorly under Mediawiki's HTML tidy. ]] local function safe_for_italics (str) if not is_set (str) then return str end if str:sub (1, 1) == "'" then str = "<span></span>" .. str; end if str:sub (-1, -1) == "'" then str = str .. "<span></span>"; end return str:gsub ('\n', ' '); -- Remove newlines as they break italics. end --[[--------------------------< W R A P _ S T Y L E >---------------------------------------------------------- Applies styling to various parameters. Supplied string is wrapped using a message_list configuration taking one argument; protects italic styled parameters. Additional text taken from citation_config.presentation - the reason this function is similar to but separate from wrap_msg(). ]] local function wrap_style (key, str) if not is_set (str) then return ""; elseif in_array (key, {'italic-title', 'trans-italic-title'}) then str = safe_for_italics (str); end return substitute (cfg.presentation[key], {str}); end --[[--------------------------< M A K E _ S E P _ L I S T >------------------------------------------------------------ make a separated list of items using provided separators. <sep_list> - typically '<comma><space>' <sep_list_pair> - typically '<space>and<space>' <sep_list_end> - typically '<comma><space>and<space>' or '<comma><space>&<space>' defaults to cfg.presentation['sep_list'], cfg.presentation['sep_list_pair'], and cfg.presentation['sep_list_end'] if <sep_list_end> is specified, <sep_list> and <sep_list_pair> must also be supplied ]] local function make_sep_list (count, list_seq, sep_list, sep_list_pair, sep_list_end) local list = ''; if not sep_list then -- set the defaults sep_list = cfg.presentation['sep_list']; sep_list_pair = cfg.presentation['sep_list_pair']; sep_list_end = cfg.presentation['sep_list_end']; end if 2 >= count then list = table.concat (list_seq, sep_list_pair); -- insert separator between two items; returns list_seq[1] then only one item elseif 2 < count then list = table.concat (list_seq, sep_list, 1, count - 1); -- concatenate all but last item with plain list separator list = table.concat ({list, list_seq[count]}, sep_list_end); -- concatenate last item onto end of <list> with final separator end return list; end --[[--------------------------< S E L E C T _ O N E >---------------------------------------------------------- Chooses one matching parameter from a list of parameters to consider. The list of parameters to consider is just names. For parameters that may be enumerated, the position of the numerator in the parameter name is identified by the '#' so |author-last1= and |author1-last= are represented as 'author-last#' and 'author#-last'. Because enumerated parameter |<param>1= is an alias of |<param>= we must test for both possibilities. Generates an error if more than one match is present. ]] local function select_one (args, aliases_list, error_condition, index) local value = nil; -- the value assigned to the selected parameter local selected = ''; -- the name of the parameter we have chosen local error_list = {}; if index ~= nil then index = tostring(index); end for _, alias in ipairs (aliases_list) do -- for each alias in the aliases list if alias:match ('#') then -- if this alias can be enumerated if '1' == index then -- when index is 1 test for enumerated and non-enumerated aliases value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- first test for non-enumerated alias end value, selected = is_alias_used (args, alias, index, true, value, selected, error_list); -- test for enumerated alias else value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- test for non-enumerated alias end end if #error_list > 0 and 'none' ~= error_condition then -- for cases where this code is used outside of extract_names() for i, v in ipairs (error_list) do error_list[i] = wrap_style ('parameter', v); end table.insert (error_list, wrap_style ('parameter', selected)); set_message (error_condition, {make_sep_list (#error_list, error_list)}); end return value, selected; end --[=[-------------------------< R E M O V E _ W I K I _ L I N K >---------------------------------------------- Gets the display text from a wikilink like [[A|B]] or [[B]] gives B The str:gsub() returns either A|B froma [[A|B]] or B from [[B]] or B from B (no wikilink markup). In l(), l:gsub() removes the link and pipe (if they exist); the second :gsub() trims whitespace from the label if str was wrapped in wikilink markup. Presumably, this is because without wikimarkup in str, there is no match in the initial gsub, the replacement function l() doesn't get called. ]=] local function remove_wiki_link (str) return (str:gsub ("%[%[([^%[%]]*)%]%]", function(l) return l:gsub ("^[^|]*|(.*)$", "%1" ):gsub ("^%s*(.-)%s*$", "%1"); end)); end --[=[-------------------------< I S _ W I K I L I N K >-------------------------------------------------------- Determines if str is a wikilink, extracts, and returns the wikilink type, link text, and display text parts. If str is a complex wikilink ([[L|D]]): returns wl_type 2 and D and L from [[L|D]]; if str is a simple wikilink ([[D]]) returns wl_type 1 and D from [[D]] and L as empty string; if not a wikilink: returns wl_type 0, str as D, and L as empty string. trims leading and trailing whitespace and pipes from L and D ([[L|]] and [[|D]] are accepted by MediaWiki and treated like [[D]]; while [[|D|]] is not accepted by MediaWiki, here, we accept it and return D without the pipes). ]=] local function is_wikilink (str) local D, L local wl_type = 2; -- assume that str is a complex wikilink [[L|D]] if not str:match ('^%[%[[^%]]+%]%]$') then -- is str some sort of a wikilink (must have some sort of content) return 0, str, ''; -- not a wikilink; return wl_type as 0, str as D, and empty string as L end L, D = str:match ('^%[%[([^|]+)|([^%]]+)%]%]$'); -- get L and D from [[L|D]] if not is_set (D) then -- if no separate display D = str:match ('^%[%[([^%]]*)|*%]%]$'); -- get D from [[D]] or [[D|]] wl_type = 1; end D = mw.text.trim (D, '%s|'); -- trim white space and pipe characters return wl_type, D, L or ''; end --[[--------------------------< S T R I P _ A P O S T R O P H E _ M A R K U P >-------------------------------- Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata. This function strips common patterns of apostrophe markup. We presume that editors who have taken the time to markup a title have, as a result, provided valid markup. When they don't, some single apostrophes are left behind. Returns the argument without wiki markup and a number; the number is more-or-less meaningless except as a flag to indicate that markup was replaced; do not rely on it as an indicator of how many of any kind of markup was removed; returns the argument and nil when no markup removed ]] local function strip_apostrophe_markup (argument) if not is_set (argument) then return argument, nil; -- no argument, nothing to do end if nil == argument:find ( "''", 1, true ) then -- Is there at least one double apostrophe? If not, exit. return argument, nil; end local flag; while true do if argument:find ("'''''", 1, true) then -- bold italic (5) argument, flag = argument:gsub ("%'%'%'%'%'", ""); -- remove all instances of it elseif argument:find ("''''", 1, true) then -- italic start and end without content (4) argument, flag=argument:gsub ("%'%'%'%'", ""); elseif argument:find ("'''", 1, true) then -- bold (3) argument, flag=argument:gsub ("%'%'%'", ""); elseif argument:find ("''", 1, true) then -- italic (2) argument, flag = argument:gsub ("%'%'", ""); else break; end end return argument, flag; -- done end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local cfg table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr) cfg = cfg_table_ptr; end --[[--------------------------< E X P O R T S >---------------------------------------------------------------- ]] return { add_maint_cat = add_maint_cat, -- exported functions add_prop_cat = add_prop_cat, error_comment = error_comment, has_accept_as_written = has_accept_as_written, hyphen_to_dash = hyphen_to_dash, in_array = in_array, is_set = is_set, is_wikilink = is_wikilink, make_sep_list = make_sep_list, make_wikilink = make_wikilink, remove_wiki_link = remove_wiki_link, safe_for_italics = safe_for_italics, select_one = select_one, set_message = set_message, set_selected_modules = set_selected_modules, strip_apostrophe_markup = strip_apostrophe_markup, substitute = substitute, wrap_style = wrap_style, z = z, -- exported table } b006801b48981b2987f20fc09cbe0dfda525e044 Module:Documentation/config 828 361 716 2022-01-25T23:46:11Z wikipedia>Ianblair23 0 link Scribunto text/plain ---------------------------------------------------------------------------------------------------- -- -- Configuration for Module:Documentation -- -- Here you can set the values of the parameters and messages used in Module:Documentation to -- localise it to your wiki and your language. Unless specified otherwise, values given here -- should be string values. ---------------------------------------------------------------------------------------------------- local cfg = {} -- Do not edit this line. ---------------------------------------------------------------------------------------------------- -- Protection template configuration ---------------------------------------------------------------------------------------------------- -- cfg['protection-reason-edit'] -- The protection reason for edit-protected templates to pass to -- [[Module:Protection banner]]. cfg['protection-reason-edit'] = 'template' --[[ ---------------------------------------------------------------------------------------------------- -- Sandbox notice configuration -- -- On sandbox pages the module can display a template notifying users that the current page is a -- sandbox, and the location of test cases pages, etc. The module decides whether the page is a -- sandbox or not based on the value of cfg['sandbox-subpage']. The following settings configure the -- messages that the notices contains. ---------------------------------------------------------------------------------------------------- --]] -- cfg['sandbox-notice-image'] -- The image displayed in the sandbox notice. cfg['sandbox-notice-image'] = '[[File:Sandbox.svg|50px|alt=|link=]]' --[[ -- cfg['sandbox-notice-pagetype-template'] -- cfg['sandbox-notice-pagetype-module'] -- cfg['sandbox-notice-pagetype-other'] -- The page type of the sandbox page. The message that is displayed depends on the current subject -- namespace. This message is used in either cfg['sandbox-notice-blurb'] or -- cfg['sandbox-notice-diff-blurb']. --]] cfg['sandbox-notice-pagetype-template'] = '[[Wikipedia:Template test cases|template sandbox]] page' cfg['sandbox-notice-pagetype-module'] = '[[Wikipedia:Template test cases|module sandbox]] page' cfg['sandbox-notice-pagetype-other'] = 'sandbox page' --[[ -- cfg['sandbox-notice-blurb'] -- cfg['sandbox-notice-diff-blurb'] -- cfg['sandbox-notice-diff-display'] -- Either cfg['sandbox-notice-blurb'] or cfg['sandbox-notice-diff-blurb'] is the opening sentence -- of the sandbox notice. The latter has a diff link, but the former does not. $1 is the page -- type, which is either cfg['sandbox-notice-pagetype-template'], -- cfg['sandbox-notice-pagetype-module'] or cfg['sandbox-notice-pagetype-other'] depending what -- namespace we are in. $2 is a link to the main template page, and $3 is a diff link between -- the sandbox and the main template. The display value of the diff link is set by -- cfg['sandbox-notice-compare-link-display']. --]] cfg['sandbox-notice-blurb'] = 'This is the $1 for $2.' cfg['sandbox-notice-diff-blurb'] = 'This is the $1 for $2 ($3).' cfg['sandbox-notice-compare-link-display'] = 'diff' --[[ -- cfg['sandbox-notice-testcases-blurb'] -- cfg['sandbox-notice-testcases-link-display'] -- cfg['sandbox-notice-testcases-run-blurb'] -- cfg['sandbox-notice-testcases-run-link-display'] -- cfg['sandbox-notice-testcases-blurb'] is a sentence notifying the user that there is a test cases page -- corresponding to this sandbox that they can edit. $1 is a link to the test cases page. -- cfg['sandbox-notice-testcases-link-display'] is the display value for that link. -- cfg['sandbox-notice-testcases-run-blurb'] is a sentence notifying the user that there is a test cases page -- corresponding to this sandbox that they can edit, along with a link to run it. $1 is a link to the test -- cases page, and $2 is a link to the page to run it. -- cfg['sandbox-notice-testcases-run-link-display'] is the display value for the link to run the test -- cases. --]] cfg['sandbox-notice-testcases-blurb'] = 'See also the companion subpage for $1.' cfg['sandbox-notice-testcases-link-display'] = 'test cases' cfg['sandbox-notice-testcases-run-blurb'] = 'See also the companion subpage for $1 ($2).' cfg['sandbox-notice-testcases-run-link-display'] = 'run' -- cfg['sandbox-category'] -- A category to add to all template sandboxes. cfg['sandbox-category'] = 'Template sandboxes' ---------------------------------------------------------------------------------------------------- -- Start box configuration ---------------------------------------------------------------------------------------------------- -- cfg['documentation-icon-wikitext'] -- The wikitext for the icon shown at the top of the template. cfg['documentation-icon-wikitext'] = '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]' -- cfg['template-namespace-heading'] -- The heading shown in the template namespace. cfg['template-namespace-heading'] = 'Template documentation' -- cfg['module-namespace-heading'] -- The heading shown in the module namespace. cfg['module-namespace-heading'] = 'Module documentation' -- cfg['file-namespace-heading'] -- The heading shown in the file namespace. cfg['file-namespace-heading'] = 'Summary' -- cfg['other-namespaces-heading'] -- The heading shown in other namespaces. cfg['other-namespaces-heading'] = 'Documentation' -- cfg['view-link-display'] -- The text to display for "view" links. cfg['view-link-display'] = 'view' -- cfg['edit-link-display'] -- The text to display for "edit" links. cfg['edit-link-display'] = 'edit' -- cfg['history-link-display'] -- The text to display for "history" links. cfg['history-link-display'] = 'history' -- cfg['purge-link-display'] -- The text to display for "purge" links. cfg['purge-link-display'] = 'purge' -- cfg['create-link-display'] -- The text to display for "create" links. cfg['create-link-display'] = 'create' ---------------------------------------------------------------------------------------------------- -- Link box (end box) configuration ---------------------------------------------------------------------------------------------------- -- cfg['transcluded-from-blurb'] -- Notice displayed when the docs are transcluded from another page. $1 is a wikilink to that page. cfg['transcluded-from-blurb'] = 'The above [[Wikipedia:Template documentation|documentation]] is [[Help:Transclusion|transcluded]] from $1.' --[[ -- cfg['create-module-doc-blurb'] -- Notice displayed in the module namespace when the documentation subpage does not exist. -- $1 is a link to create the documentation page with the preload cfg['module-preload'] and the -- display cfg['create-link-display']. --]] cfg['create-module-doc-blurb'] = 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].' ---------------------------------------------------------------------------------------------------- -- Experiment blurb configuration ---------------------------------------------------------------------------------------------------- --[[ -- cfg['experiment-blurb-template'] -- cfg['experiment-blurb-module'] -- The experiment blurb is the text inviting editors to experiment in sandbox and test cases pages. -- It is only shown in the template and module namespaces. With the default English settings, it -- might look like this: -- -- Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages. -- -- In this example, "sandbox", "edit", "diff", "testcases", and "edit" would all be links. -- -- There are two versions, cfg['experiment-blurb-template'] and cfg['experiment-blurb-module'], depending -- on what namespace we are in. -- -- Parameters: -- -- $1 is a link to the sandbox page. If the sandbox exists, it is in the following format: -- -- cfg['sandbox-link-display'] (cfg['sandbox-edit-link-display'] | cfg['compare-link-display']) -- -- If the sandbox doesn't exist, it is in the format: -- -- cfg['sandbox-link-display'] (cfg['sandbox-create-link-display'] | cfg['mirror-link-display']) -- -- The link for cfg['sandbox-create-link-display'] link preloads the page with cfg['template-sandbox-preload'] -- or cfg['module-sandbox-preload'], depending on the current namespace. The link for cfg['mirror-link-display'] -- loads a default edit summary of cfg['mirror-edit-summary']. -- -- $2 is a link to the test cases page. If the test cases page exists, it is in the following format: -- -- cfg['testcases-link-display'] (cfg['testcases-edit-link-display'] | cfg['testcases-run-link-display']) -- -- If the test cases page doesn't exist, it is in the format: -- -- cfg['testcases-link-display'] (cfg['testcases-create-link-display']) -- -- If the test cases page doesn't exist, the link for cfg['testcases-create-link-display'] preloads the -- page with cfg['template-testcases-preload'] or cfg['module-testcases-preload'], depending on the current -- namespace. --]] cfg['experiment-blurb-template'] = "Editors can experiment in this template's $1 and $2 pages." cfg['experiment-blurb-module'] = "Editors can experiment in this module's $1 and $2 pages." ---------------------------------------------------------------------------------------------------- -- Sandbox link configuration ---------------------------------------------------------------------------------------------------- -- cfg['sandbox-subpage'] -- The name of the template subpage typically used for sandboxes. cfg['sandbox-subpage'] = 'sandbox' -- cfg['template-sandbox-preload'] -- Preload file for template sandbox pages. cfg['template-sandbox-preload'] = 'Template:Documentation/preload-sandbox' -- cfg['module-sandbox-preload'] -- Preload file for Lua module sandbox pages. cfg['module-sandbox-preload'] = 'Template:Documentation/preload-module-sandbox' -- cfg['sandbox-link-display'] -- The text to display for "sandbox" links. cfg['sandbox-link-display'] = 'sandbox' -- cfg['sandbox-edit-link-display'] -- The text to display for sandbox "edit" links. cfg['sandbox-edit-link-display'] = 'edit' -- cfg['sandbox-create-link-display'] -- The text to display for sandbox "create" links. cfg['sandbox-create-link-display'] = 'create' -- cfg['compare-link-display'] -- The text to display for "compare" links. cfg['compare-link-display'] = 'diff' -- cfg['mirror-edit-summary'] -- The default edit summary to use when a user clicks the "mirror" link. $1 is a wikilink to the -- template page. cfg['mirror-edit-summary'] = 'Create sandbox version of $1' -- cfg['mirror-link-display'] -- The text to display for "mirror" links. cfg['mirror-link-display'] = 'mirror' -- cfg['mirror-link-preload'] -- The page to preload when a user clicks the "mirror" link. cfg['mirror-link-preload'] = 'Template:Documentation/mirror' ---------------------------------------------------------------------------------------------------- -- Test cases link configuration ---------------------------------------------------------------------------------------------------- -- cfg['testcases-subpage'] -- The name of the template subpage typically used for test cases. cfg['testcases-subpage'] = 'testcases' -- cfg['template-testcases-preload'] -- Preload file for template test cases pages. cfg['template-testcases-preload'] = 'Template:Documentation/preload-testcases' -- cfg['module-testcases-preload'] -- Preload file for Lua module test cases pages. cfg['module-testcases-preload'] = 'Template:Documentation/preload-module-testcases' -- cfg['testcases-link-display'] -- The text to display for "testcases" links. cfg['testcases-link-display'] = 'testcases' -- cfg['testcases-edit-link-display'] -- The text to display for test cases "edit" links. cfg['testcases-edit-link-display'] = 'edit' -- cfg['testcases-run-link-display'] -- The text to display for test cases "run" links. cfg['testcases-run-link-display'] = 'run' -- cfg['testcases-create-link-display'] -- The text to display for test cases "create" links. cfg['testcases-create-link-display'] = 'create' ---------------------------------------------------------------------------------------------------- -- Add categories blurb configuration ---------------------------------------------------------------------------------------------------- --[[ -- cfg['add-categories-blurb'] -- Text to direct users to add categories to the /doc subpage. Not used if the "content" or -- "docname fed" arguments are set, as then it is not clear where to add the categories. $1 is a -- link to the /doc subpage with a display value of cfg['doc-link-display']. --]] cfg['add-categories-blurb'] = 'Add categories to the $1 subpage.' -- cfg['doc-link-display'] -- The text to display when linking to the /doc subpage. cfg['doc-link-display'] = '/doc' ---------------------------------------------------------------------------------------------------- -- Subpages link configuration ---------------------------------------------------------------------------------------------------- --[[ -- cfg['subpages-blurb'] -- The "Subpages of this template" blurb. $1 is a link to the main template's subpages with a -- display value of cfg['subpages-link-display']. In the English version this blurb is simply -- the link followed by a period, and the link display provides the actual text. --]] cfg['subpages-blurb'] = '$1.' --[[ -- cfg['subpages-link-display'] -- The text to display for the "subpages of this page" link. $1 is cfg['template-pagetype'], -- cfg['module-pagetype'] or cfg['default-pagetype'], depending on whether the current page is in -- the template namespace, the module namespace, or another namespace. --]] cfg['subpages-link-display'] = 'Subpages of this $1' -- cfg['template-pagetype'] -- The pagetype to display for template pages. cfg['template-pagetype'] = 'template' -- cfg['module-pagetype'] -- The pagetype to display for Lua module pages. cfg['module-pagetype'] = 'module' -- cfg['default-pagetype'] -- The pagetype to display for pages other than templates or Lua modules. cfg['default-pagetype'] = 'page' ---------------------------------------------------------------------------------------------------- -- Doc link configuration ---------------------------------------------------------------------------------------------------- -- cfg['doc-subpage'] -- The name of the subpage typically used for documentation pages. cfg['doc-subpage'] = 'doc' -- cfg['docpage-preload'] -- Preload file for template documentation pages in all namespaces. cfg['docpage-preload'] = 'Template:Documentation/preload' -- cfg['module-preload'] -- Preload file for Lua module documentation pages. cfg['module-preload'] = 'Template:Documentation/preload-module-doc' ---------------------------------------------------------------------------------------------------- -- HTML and CSS configuration ---------------------------------------------------------------------------------------------------- -- cfg['templatestyles'] -- The name of the TemplateStyles page where CSS is kept. -- Sandbox CSS will be at Module:Documentation/sandbox/styles.css when needed. cfg['templatestyles'] = 'Module:Documentation/styles.css' -- cfg['container'] -- Class which can be used to set flex or grid CSS on the -- two child divs documentation and documentation-metadata cfg['container'] = 'documentation-container' -- cfg['main-div-classes'] -- Classes added to the main HTML "div" tag. cfg['main-div-classes'] = 'documentation' -- cfg['main-div-heading-class'] -- Class for the main heading for templates and modules and assoc. talk spaces cfg['main-div-heading-class'] = 'documentation-heading' -- cfg['start-box-class'] -- Class for the start box cfg['start-box-class'] = 'documentation-startbox' -- cfg['start-box-link-classes'] -- Classes used for the [view][edit][history] or [create] links in the start box. -- mw-editsection-like is per [[Wikipedia:Village pump (technical)/Archive 117]] cfg['start-box-link-classes'] = 'mw-editsection-like plainlinks' -- cfg['end-box-class'] -- Class for the end box. cfg['end-box-class'] = 'documentation-metadata' -- cfg['end-box-plainlinks'] -- Plainlinks cfg['end-box-plainlinks'] = 'plainlinks' -- cfg['toolbar-class'] -- Class added for toolbar links. cfg['toolbar-class'] = 'documentation-toolbar' -- cfg['clear'] -- Just used to clear things. cfg['clear'] = 'documentation-clear' ---------------------------------------------------------------------------------------------------- -- Tracking category configuration ---------------------------------------------------------------------------------------------------- -- cfg['display-strange-usage-category'] -- Set to true to enable output of cfg['strange-usage-category'] if the module is used on a /doc subpage -- or a /testcases subpage. This should be a boolean value (either true or false). cfg['display-strange-usage-category'] = true -- cfg['strange-usage-category'] -- Category to output if cfg['display-strange-usage-category'] is set to true and the module is used on a -- /doc subpage or a /testcases subpage. cfg['strange-usage-category'] = 'Wikipedia pages with strange ((documentation)) usage' --[[ ---------------------------------------------------------------------------------------------------- -- End configuration -- -- Don't edit anything below this line. ---------------------------------------------------------------------------------------------------- --]] return cfg 71b68ed73088f1a59d61acf06bbee9fde6677f03 Module:TableTools 828 353 700 2022-01-31T13:08:18Z wikipedia>MSGJ 0 updates/fixes requested by [[User:Uzume]] Scribunto text/plain ------------------------------------------------------------------------------------ -- TableTools -- -- -- -- This module includes a number of functions for dealing with Lua tables. -- -- It is a meta-module, meant to be called from other Lua modules, and should not -- -- be called directly from #invoke. -- ------------------------------------------------------------------------------------ local libraryUtil = require('libraryUtil') local p = {} -- Define often-used variables and functions. local floor = math.floor local infinity = math.huge local checkType = libraryUtil.checkType local checkTypeMulti = libraryUtil.checkTypeMulti ------------------------------------------------------------------------------------ -- isPositiveInteger -- -- This function returns true if the given value is a positive integer, and false -- if not. Although it doesn't operate on tables, it is included here as it is -- useful for determining whether a given table key is in the array part or the -- hash part of a table. ------------------------------------------------------------------------------------ function p.isPositiveInteger(v) return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity end ------------------------------------------------------------------------------------ -- isNan -- -- This function returns true if the given number is a NaN value, and false if -- not. Although it doesn't operate on tables, it is included here as it is useful -- for determining whether a value can be a valid table key. Lua will generate an -- error if a NaN is used as a table key. ------------------------------------------------------------------------------------ function p.isNan(v) return type(v) == 'number' and v ~= v end ------------------------------------------------------------------------------------ -- shallowClone -- -- This returns a clone of a table. The value returned is a new table, but all -- subtables and functions are shared. Metamethods are respected, but the returned -- table will have no metatable of its own. ------------------------------------------------------------------------------------ function p.shallowClone(t) checkType('shallowClone', 1, t, 'table') local ret = {} for k, v in pairs(t) do ret[k] = v end return ret end ------------------------------------------------------------------------------------ -- removeDuplicates -- -- This removes duplicate values from an array. Non-positive-integer keys are -- ignored. The earliest value is kept, and all subsequent duplicate values are -- removed, but otherwise the array order is unchanged. ------------------------------------------------------------------------------------ function p.removeDuplicates(arr) checkType('removeDuplicates', 1, arr, 'table') local isNan = p.isNan local ret, exists = {}, {} for _, v in ipairs(arr) do if isNan(v) then -- NaNs can't be table keys, and they are also unique, so we don't need to check existence. ret[#ret + 1] = v else if not exists[v] then ret[#ret + 1] = v exists[v] = true end end end return ret end ------------------------------------------------------------------------------------ -- numKeys -- -- This takes a table and returns an array containing the numbers of any numerical -- keys that have non-nil values, sorted in numerical order. ------------------------------------------------------------------------------------ function p.numKeys(t) checkType('numKeys', 1, t, 'table') local isPositiveInteger = p.isPositiveInteger local nums = {} for k in pairs(t) do if isPositiveInteger(k) then nums[#nums + 1] = k end end table.sort(nums) return nums end ------------------------------------------------------------------------------------ -- affixNums -- -- This takes a table and returns an array containing the numbers of keys with the -- specified prefix and suffix. For example, for the table -- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will return -- {1, 3, 6}. ------------------------------------------------------------------------------------ function p.affixNums(t, prefix, suffix) checkType('affixNums', 1, t, 'table') checkType('affixNums', 2, prefix, 'string', true) checkType('affixNums', 3, suffix, 'string', true) local function cleanPattern(s) -- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally. return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1') end prefix = prefix or '' suffix = suffix or '' prefix = cleanPattern(prefix) suffix = cleanPattern(suffix) local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$' local nums = {} for k in pairs(t) do if type(k) == 'string' then local num = mw.ustring.match(k, pattern) if num then nums[#nums + 1] = tonumber(num) end end end table.sort(nums) return nums end ------------------------------------------------------------------------------------ -- numData -- -- Given a table with keys like {"foo1", "bar1", "foo2", "baz2"}, returns a table -- of subtables in the format -- {[1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'}}. -- Keys that don't end with an integer are stored in a subtable named "other". The -- compress option compresses the table so that it can be iterated over with -- ipairs. ------------------------------------------------------------------------------------ function p.numData(t, compress) checkType('numData', 1, t, 'table') checkType('numData', 2, compress, 'boolean', true) local ret = {} for k, v in pairs(t) do local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$') if num then num = tonumber(num) local subtable = ret[num] or {} if prefix == '' then -- Positional parameters match the blank string; put them at the start of the subtable instead. prefix = 1 end subtable[prefix] = v ret[num] = subtable else local subtable = ret.other or {} subtable[k] = v ret.other = subtable end end if compress then local other = ret.other ret = p.compressSparseArray(ret) ret.other = other end return ret end ------------------------------------------------------------------------------------ -- compressSparseArray -- -- This takes an array with one or more nil values, and removes the nil values -- while preserving the order, so that the array can be safely traversed with -- ipairs. ------------------------------------------------------------------------------------ function p.compressSparseArray(t) checkType('compressSparseArray', 1, t, 'table') local ret = {} local nums = p.numKeys(t) for _, num in ipairs(nums) do ret[#ret + 1] = t[num] end return ret end ------------------------------------------------------------------------------------ -- sparseIpairs -- -- This is an iterator for sparse arrays. It can be used like ipairs, but can -- handle nil values. ------------------------------------------------------------------------------------ function p.sparseIpairs(t) checkType('sparseIpairs', 1, t, 'table') local nums = p.numKeys(t) local i = 0 local lim = #nums return function () i = i + 1 if i <= lim then local key = nums[i] return key, t[key] else return nil, nil end end end ------------------------------------------------------------------------------------ -- size -- -- This returns the size of a key/value pair table. It will also work on arrays, -- but for arrays it is more efficient to use the # operator. ------------------------------------------------------------------------------------ function p.size(t) checkType('size', 1, t, 'table') local i = 0 for _ in pairs(t) do i = i + 1 end return i end local function defaultKeySort(item1, item2) -- "number" < "string", so numbers will be sorted before strings. local type1, type2 = type(item1), type(item2) if type1 ~= type2 then return type1 < type2 elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then return tostring(item1) < tostring(item2) else return item1 < item2 end end ------------------------------------------------------------------------------------ -- keysToList -- -- Returns an array of the keys in a table, sorted using either a default -- comparison function or a custom keySort function. ------------------------------------------------------------------------------------ function p.keysToList(t, keySort, checked) if not checked then checkType('keysToList', 1, t, 'table') checkTypeMulti('keysToList', 2, keySort, {'function', 'boolean', 'nil'}) end local arr = {} local index = 1 for k in pairs(t) do arr[index] = k index = index + 1 end if keySort ~= false then keySort = type(keySort) == 'function' and keySort or defaultKeySort table.sort(arr, keySort) end return arr end ------------------------------------------------------------------------------------ -- sortedPairs -- -- Iterates through a table, with the keys sorted using the keysToList function. -- If there are only numerical keys, sparseIpairs is probably more efficient. ------------------------------------------------------------------------------------ function p.sortedPairs(t, keySort) checkType('sortedPairs', 1, t, 'table') checkType('sortedPairs', 2, keySort, 'function', true) local arr = p.keysToList(t, keySort, true) local i = 0 return function () i = i + 1 local key = arr[i] if key ~= nil then return key, t[key] else return nil, nil end end end ------------------------------------------------------------------------------------ -- isArray -- -- Returns true if the given value is a table and all keys are consecutive -- integers starting at 1. ------------------------------------------------------------------------------------ function p.isArray(v) if type(v) ~= 'table' then return false end local i = 0 for _ in pairs(v) do i = i + 1 if v[i] == nil then return false end end return true end ------------------------------------------------------------------------------------ -- isArrayLike -- -- Returns true if the given value is iterable and all keys are consecutive -- integers starting at 1. ------------------------------------------------------------------------------------ function p.isArrayLike(v) if not pcall(pairs, v) then return false end local i = 0 for _ in pairs(v) do i = i + 1 if v[i] == nil then return false end end return true end ------------------------------------------------------------------------------------ -- invert -- -- Transposes the keys and values in an array. For example, {"a", "b", "c"} -> -- {a = 1, b = 2, c = 3}. Duplicates are not supported (result values refer to -- the index of the last duplicate) and NaN values are ignored. ------------------------------------------------------------------------------------ function p.invert(arr) checkType("invert", 1, arr, "table") local isNan = p.isNan local map = {} for i, v in ipairs(arr) do if not isNan(v) then map[v] = i end end return map end ------------------------------------------------------------------------------------ -- listToSet -- -- Creates a set from the array part of the table. Indexing the set by any of the -- values of the array returns true. For example, {"a", "b", "c"} -> -- {a = true, b = true, c = true}. NaN values are ignored as Lua considers them -- never equal to any value (including other NaNs or even themselves). ------------------------------------------------------------------------------------ function p.listToSet(arr) checkType("listToSet", 1, arr, "table") local isNan = p.isNan local set = {} for _, v in ipairs(arr) do if not isNan(v) then set[v] = true end end return set end ------------------------------------------------------------------------------------ -- deepCopy -- -- Recursive deep copy function. Preserves identities of subtables. ------------------------------------------------------------------------------------ local function _deepCopy(orig, includeMetatable, already_seen) -- Stores copies of tables indexed by the original table. already_seen = already_seen or {} local copy = already_seen[orig] if copy ~= nil then return copy end if type(orig) == 'table' then copy = {} for orig_key, orig_value in pairs(orig) do copy[_deepCopy(orig_key, includeMetatable, already_seen)] = _deepCopy(orig_value, includeMetatable, already_seen) end already_seen[orig] = copy if includeMetatable then local mt = getmetatable(orig) if mt ~= nil then local mt_copy = _deepCopy(mt, includeMetatable, already_seen) setmetatable(copy, mt_copy) already_seen[mt] = mt_copy end end else -- number, string, boolean, etc copy = orig end return copy end function p.deepCopy(orig, noMetatable, already_seen) checkType("deepCopy", 3, already_seen, "table", true) return _deepCopy(orig, not noMetatable, already_seen) end ------------------------------------------------------------------------------------ -- sparseConcat -- -- Concatenates all values in the table that are indexed by a number, in order. -- sparseConcat{a, nil, c, d} => "acd" -- sparseConcat{nil, b, c, d} => "bcd" ------------------------------------------------------------------------------------ function p.sparseConcat(t, sep, i, j) local arr = {} local arr_i = 0 for _, v in p.sparseIpairs(t) do arr_i = arr_i + 1 arr[arr_i] = v end return table.concat(arr, sep, i, j) end ------------------------------------------------------------------------------------ -- length -- -- Finds the length of an array, or of a quasi-array with keys such as "data1", -- "data2", etc., using an exponential search algorithm. It is similar to the -- operator #, but may return a different value when there are gaps in the array -- portion of the table. Intended to be used on data loaded with mw.loadData. For -- other tables, use #. -- Note: #frame.args in frame object always be set to 0, regardless of the number -- of unnamed template parameters, so use this function for frame.args. ------------------------------------------------------------------------------------ function p.length(t, prefix) -- requiring module inline so that [[Module:Exponential search]] which is -- only needed by this one function doesn't get millions of transclusions local expSearch = require("Module:Exponential search") checkType('length', 1, t, 'table') checkType('length', 2, prefix, 'string', true) return expSearch(function (i) local key if prefix then key = prefix .. tostring(i) else key = i end return t[key] ~= nil end) or 0 end ------------------------------------------------------------------------------------ -- inArray -- -- Returns true if valueToFind is a member of the array, and false otherwise. ------------------------------------------------------------------------------------ function p.inArray(arr, valueToFind) checkType("inArray", 1, arr, "table") -- if valueToFind is nil, error? for _, v in ipairs(arr) do if v == valueToFind then return true end end return false end return p 085e7094ac84eb0132ee65822cf3f69cd8ba3d81 Template:Short description/lowercasecheck 10 412 824 2022-02-12T16:35:05Z wikipedia>ToBeFree 0 Changed protection settings for "[[Template:Short description/lowercasecheck]]": 4 million transclusions, through [[Template:Short description]] ([[WP:HRT]]) ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) wikitext text/x-wiki {{#ifeq:<!--test first character for lower-case letter-->{{#invoke:string|find|1={{{1|}}}|2=^%l|plain=false}}|1 |<!-- first character is a lower case letter; test against whitelist -->{{#switch: {{First word|{{{1|}}}}}<!--begin whitelist--> |c. <!--for circa--> |gTLD |iMac |iOS |iOS, |iPad |iPhone |iTunes |macOS |none |pH |pH-dependent=<!-- end whitelist; short description starts with an allowed lower-case string; whitelist matched; do nothing --> |#default=<!-- apply category to track lower-case short descriptions -->{{main other|[[Category:Pages with lower-case short description|{{trim|{{{1|}}}}}]]}}{{Testcases other|{{red|CATEGORY APPLIED}}}}<!-- end whitelist test -->}} |<!-- short description does not start with lower-case letter; do nothing; end lower-case test --> }}<noinclude> {{documentation}} </noinclude> 9a6d4db14b74614625fd234b4f8ee3c8e1a235c0 Module:Check for unknown parameters 828 414 828 2022-02-21T05:24:13Z wikipedia>BusterD 0 Changed protection settings for "[[Module:Check for unknown parameters]]": [[WP:High-risk templates|Highly visible template]]; requested at [[WP:RfPP]] ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) Scribunto text/plain -- This module may be used to compare the arguments passed to the parent -- with a list of arguments, returning a specified result if an argument is -- not on the list local p = {} local function trim(s) return s:match('^%s*(.-)%s*$') end local function isnotempty(s) return s and s:match('%S') end local function clean(text) -- Return text cleaned for display and truncated if too long. -- Strip markers are replaced with dummy text representing the original wikitext. local pos, truncated local function truncate(text) if truncated then return '' end if mw.ustring.len(text) > 25 then truncated = true text = mw.ustring.sub(text, 1, 25) .. '...' end return mw.text.nowiki(text) end local parts = {} for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do pos = remainder table.insert(parts, truncate(before) .. '&lt;' .. tag .. '&gt;...&lt;/' .. tag .. '&gt;') end table.insert(parts, truncate(text:sub(pos or 1))) return table.concat(parts) end function p._check(args, pargs) if type(args) ~= "table" or type(pargs) ~= "table" then -- TODO: error handling return end -- create the list of known args, regular expressions, and the return string local knownargs = {} local regexps = {} for k, v in pairs(args) do if type(k) == 'number' then v = trim(v) knownargs[v] = 1 elseif k:find('^regexp[1-9][0-9]*$') then table.insert(regexps, '^' .. v .. '$') end end -- loop over the parent args, and make sure they are on the list local ignoreblank = isnotempty(args['ignoreblank']) local showblankpos = isnotempty(args['showblankpositional']) local values = {} for k, v in pairs(pargs) do if type(k) == 'string' and knownargs[k] == nil then local knownflag = false for _, regexp in ipairs(regexps) do if mw.ustring.match(k, regexp) then knownflag = true break end end if not knownflag and ( not ignoreblank or isnotempty(v) ) then table.insert(values, clean(k)) end elseif type(k) == 'number' and knownargs[tostring(k)] == nil then local knownflag = false for _, regexp in ipairs(regexps) do if mw.ustring.match(tostring(k), regexp) then knownflag = true break end end if not knownflag and ( showblankpos or isnotempty(v) ) then table.insert(values, k .. ' = ' .. clean(v)) end end end -- add results to the output tables local res = {} if #values > 0 then local unknown_text = args['unknown'] or 'Found _VALUE_, ' if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then local preview_text = args['preview'] if isnotempty(preview_text) then preview_text = require('Module:If preview')._warning({preview_text}) elseif preview == nil then preview_text = unknown_text end unknown_text = preview_text end for _, v in pairs(values) do -- Fix odd bug for | = which gets stripped to the empty string and -- breaks category links if v == '' then v = ' ' end -- avoid error with v = 'example%2' ("invalid capture index") local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v}) table.insert(res, r) end end return table.concat(res) end function p.check(frame) local args = frame.args local pargs = frame:getParent().args return p._check(args, pargs) end return p 93db6d115d4328d2a5148bb42959105e367b663e Module:)) 828 510 1026 2022-02-23T02:26:26Z string2>Xaosflux 0 Changed protection settings for "[[Template:))]]": 100K+uses ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) wikitext text/x-wiki }}<noinclude> {{documentation}} </noinclude> 85ca77d4d6ff71d8e6396ebd798f87fa7f45dc02 Module:Effective protection expiry 828 354 702 2022-02-23T10:59:29Z wikipedia>Xaosflux 0 Changed protection settings for "[[Module:Effective protection expiry]]": used in the mediawiki interface / match [[Module:Effective protection level]] ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) Scribunto text/plain local p = {} -- Returns the expiry of a restriction of an action on a given title, or unknown if it cannot be known. -- If no title is specified, the title of the page being displayed is used. function p._main(action, pagename) local title if type(pagename) == 'table' and pagename.prefixedText then title = pagename elseif pagename then title = mw.title.new(pagename) else title = mw.title.getCurrentTitle() end pagename = title.prefixedText if action == 'autoreview' then local stabilitySettings = mw.ext.FlaggedRevs.getStabilitySettings(title) return stabilitySettings and stabilitySettings.expiry or 'unknown' elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' then error( 'First parameter must be one of edit, move, create, upload, autoreview', 2 ) end local rawExpiry = mw.getCurrentFrame():callParserFunction('PROTECTIONEXPIRY', action, pagename) if rawExpiry == 'infinity' then return 'infinity' elseif rawExpiry == '' then return 'unknown' else local year, month, day, hour, minute, second = rawExpiry:match( '^(%d%d%d%d)(%d%d)(%d%d)(%d%d)(%d%d)(%d%d)$' ) if year then return string.format( '%s-%s-%sT%s:%s:%s', year, month, day, hour, minute, second ) else error('internal error in Module:Effective protection expiry; malformed expiry timestamp') end end end setmetatable(p, { __index = function(t, k) return function(frame) return t._main(k, frame.args[1]) end end }) return p 9a8c58dc2667232ed08a9b206a5d89ca8150312b Module:String split 828 511 1028 2022-02-23T16:31:10Z string2>Qwerfjkl 0 Subst-able wikitext text/x-wiki {{safesubst<noinclude/>:#invoke:String2 |split}}<noinclude> {{documentation}} </noinclude> fe0e5c984dbf2d0cf923f385a3b2596010475bc6 Module:Find sources 828 426 852 2022-02-27T13:33:28Z wikipedia>Primefac 0 Changed protection settings for "[[Module:Find sources]]": re-raise, 1.5million transclusions and no edits from the EXCON editor ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain -- This module implements {{find sources}} and other similar templates, and -- also provides a mechanism to easily create new source-finding templates. -- Define constants local ROOT_PAGE = 'Module:Find sources' local TEMPLATE_ROOT = ROOT_PAGE .. '/templates/' -- for template config modules local LINK_CONFIG = ROOT_PAGE .. '/links' -- for link config modules local CONFIG_PAGE = ROOT_PAGE .. '/config' -- for global config -- Load required modules local checkType = require('libraryUtil').checkType local cfg = mw.loadData(CONFIG_PAGE) local p = {} local function maybeLoadData(page) local success, data = pcall(mw.loadData, page) return success and data end local function substituteParams(msg, ...) return mw.message.newRawMessage(msg, ...):plain() end local function renderSearchString(searchTerms, separator, transformFunc) -- This takes a table of search terms and turns it into a search string -- that can be used in a URL or in a display value. The transformFunc -- parameter can be used to transform each search term in some way (for -- example, URL-encoding them). local searchStrings = {} for i, s in ipairs(searchTerms) do searchStrings[i] = s end if transformFunc then for i, s in ipairs(searchStrings) do searchStrings[i] = transformFunc(s) end end return table.concat(searchStrings, separator) end function p._renderLink(code, searchTerms, display, tooltip) -- Renders the external link wikicode for one link, given the link code, -- a table of search terms, and an optional display value and tooltip. -- Get link config. local links = maybeLoadData(LINK_CONFIG) local linkCfg = links[code] if not linkCfg then error(string.format( "invalid link code '%s'; no link config found at [[%s]]", code, LINK_CONFIG )) end -- Make URL. local url do local separator = linkCfg.separator or "+" local searchString = renderSearchString( searchTerms, separator, mw.uri.encode ) url = substituteParams(linkCfg.url, searchString) end if tooltip then return string.format('<span title="%s" style="border-bottom: 1px dotted;">[%s %s]</span>', mw.text.encode(tooltip), url, display or linkCfg.display) else return string.format('[%s %s]', url, display or linkCfg.display) end end function p._main(template, args) -- The main access point from Lua. checkType('_main', 1, template, 'string') checkType('_main', 2, args, 'table', true) args = args or {} local title = mw.title.getCurrentTitle() -- Get the template config. local templateCfgPage = TEMPLATE_ROOT .. template local templateCfg = maybeLoadData(templateCfgPage) if not templateCfg then error(string.format( "invalid template name '%s'; no template config found at [[%s]]", template, templateCfgPage )) end -- Namespace check. if not templateCfg.isUsedInMainspace and title.namespace == 0 then local formatString = '<strong class="error">%s</strong>' if cfg['namespace-error-category'] then formatString = formatString .. '[[%s:%s]]' end return string.format( formatString, cfg['namespace-error'], mw.site.namespaces[14].name, cfg['namespace-error-category'] ) end -- Get the search terms from the arguments. local searchTerms = {} for i, s in ipairs(args) do searchTerms[i] = s end if not searchTerms[1] then -- Use the current subpage name as the default search term, unless -- another title is provided. If the page uses a disambiguator like -- "Foo (bar)", make "Foo" the first term and "bar" the second. local searchTitle = args.title or title.subpageText local term, dab = searchTitle:match('^(.*) (%b())$') if dab then dab = dab:sub(2, -2) -- Remove parens end if term and dab then searchTerms[1] = term searchTerms[2] = dab else searchTerms[1] = searchTitle end end searchTerms[1] = '"' .. searchTerms[1] .. '"' -- Make the intro link local introLink if templateCfg.introLink then local code = templateCfg.introLink.code local display = templateCfg.introLink.display or renderSearchString( searchTerms, '&nbsp;' ) local tooltip = templateCfg.introLink.tooltip introLink = p._renderLink(code, searchTerms, display, tooltip) else introLink = '' end -- Make the other links local links = {} local separator = templateCfg.separator or cfg['default-separator'] local sep = '' for i, t in ipairs(templateCfg.links) do links[i] = sep .. p._renderLink(t.code, searchTerms, t.display, t.tooltip) .. (t.afterDisplay or '') sep = t.separator or separator end links = table.concat(links) -- Make the blurb. local blurb = substituteParams(templateCfg.blurb, introLink, links) local span = mw.html.create('span') span :addClass('plainlinks') :addClass(templateCfg.class) :cssText(templateCfg.style) :wikitext(blurb) return tostring(span) end setmetatable(p, { __index = function(t, template) -- The main access point from #invoke. -- Invocations will look like {{#invoke:Find sources|template name}}, -- where "template name" is a subpage of [[Module:Find sources/templates]]. local tname = template if tname:sub(-8) == '/sandbox' then -- This makes {{Find sources/sandbox|Albert Einstein}} work. tname = tname:sub(1, -9) end return function(frame) local args = require('Module:Arguments').getArgs(frame, { wrappers = mw.site.namespaces[10].name .. ':' .. tname }) return t._main(template, args) end end}) return p 10c2d5c5a05295b49581eecba936dae039a4a290 Module:Find sources/templates/Find sources mainspace 828 428 856 2022-03-01T07:33:51Z wikipedia>Primefac 0 Changed protection settings for "[[Module:Find sources/templates/Find sources mainspace]]": match main module ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain return { blurb = "''Find sources:''&nbsp;$1&nbsp;–&nbsp;$2", introLink = { code = 'google' }, links = { { code = 'google news', display = 'news' }, { code = 'google newspapers', display = 'newspapers' }, { code = 'google books', display = 'books' }, { code = 'google scholar', display = 'scholar' }, { code = 'jstor', display = 'JSTOR' } }, isUsedInMainspace = true } abfeb1a778b232887c9400ca7667092141e60257 Module:Template link general 828 483 964 2022-03-08T08:30:51Z infoboxes>Primefac 0 update from sandbox - fixes to _show_result and adding _expand Scribunto text/plain -- This implements Template:Tlg local getArgs = require('Module:Arguments').getArgs local p = {} -- Is a string non-empty? local function _ne(s) return s ~= nil and s ~= "" end local nw = mw.text.nowiki local function addTemplate(s) local i, _ = s:find(':', 1, true) if i == nil then return 'Template:' .. s end local ns = s:sub(1, i - 1) if ns == '' or mw.site.namespaces[ns] then return s else return 'Template:' .. s end end local function trimTemplate(s) local needle = 'template:' if s:sub(1, needle:len()):lower() == needle then return s:sub(needle:len() + 1) else return s end end local function linkTitle(args) if _ne(args.nolink) then return args['1'] end local titleObj local titlePart = '[[' if args['1'] then -- This handles :Page and other NS titleObj = mw.title.new(args['1'], 'Template') else titleObj = mw.title.getCurrentTitle() end titlePart = titlePart .. (titleObj ~= nil and titleObj.fullText or addTemplate(args['1'])) local textPart = args.alttext if not _ne(textPart) then if titleObj ~= nil then textPart = titleObj:inNamespace("Template") and args['1'] or titleObj.fullText else -- redlink textPart = args['1'] end end if _ne(args.subst) then -- HACK: the ns thing above is probably broken textPart = 'subst:' .. textPart end if _ne(args.brace) then textPart = nw('{{') .. textPart .. nw('}}') elseif _ne(args.braceinside) then textPart = nw('{') .. textPart .. nw('}') end titlePart = titlePart .. '|' .. textPart .. ']]' if _ne(args.braceinside) then titlePart = nw('{') .. titlePart .. nw('}') end return titlePart end function p.main(frame) local args = getArgs(frame, { trim = true, removeBlanks = false }) return p._main(args) end function p._main(args) local bold = _ne(args.bold) or _ne(args.boldlink) or _ne(args.boldname) local italic = _ne(args.italic) or _ne(args.italics) local dontBrace = _ne(args.brace) or _ne(args.braceinside) local code = _ne(args.code) or _ne(args.tt) local show_result = _ne(args._show_result) local expand = _ne(args._expand) -- Build the link part local titlePart = linkTitle(args) if bold then titlePart = "'''" .. titlePart .. "'''" end if _ne(args.nowrapname) then titlePart = '<span class="nowrap">' .. titlePart .. '</span>' end -- Build the arguments local textPart = "" local textPartBuffer = "&#124;" local codeArguments = {} local codeArgumentsString = "" local i = 2 local j = 1 while args[i] do local val = args[i] if val ~= "" then if _ne(args.nowiki) then -- Unstrip nowiki tags first because calling nw on something that already contains nowiki tags will -- mangle the nowiki strip marker and result in literal UNIQ...QINU showing up val = nw(mw.text.unstripNoWiki(val)) end local k, v = string.match(val, "(.*)=(.*)") if not k then codeArguments[j] = val j = j + 1 else codeArguments[k] = v end codeArgumentsString = codeArgumentsString .. textPartBuffer .. val if italic then val = '<span style="font-style:italic;">' .. val .. '</span>' end textPart = textPart .. textPartBuffer .. val end i = i + 1 end -- final wrap local ret = titlePart .. textPart if not dontBrace then ret = nw('{{') .. ret .. nw('}}') end if _ne(args.a) then ret = nw('*') .. '&nbsp;' .. ret end if _ne(args.kbd) then ret = '<kbd>' .. ret .. '</kbd>' end if code then ret = '<code>' .. ret .. '</code>' elseif _ne(args.plaincode) then ret = '<code style="border:none;background:transparent;">' .. ret .. '</code>' end if _ne(args.nowrap) then ret = '<span class="nowrap">' .. ret .. '</span>' end --[[ Wrap as html?? local span = mw.html.create('span') span:wikitext(ret) --]] if _ne(args.debug) then ret = ret .. '\n<pre>' .. mw.text.encode(mw.dumpObject(args)) .. '</pre>' end if show_result then local result = mw.getCurrentFrame():expandTemplate{title = addTemplate(args[1]), args = codeArguments} ret = ret .. " → " .. result end if expand then local query = mw.text.encode('{{' .. addTemplate(args[1]) .. string.gsub(codeArgumentsString, textPartBuffer, "|") .. '}}') local url = mw.uri.fullUrl('special:ExpandTemplates', 'wpInput=' .. query) mw.log() ret = ret .. " [" .. tostring(url) .. "]" end return ret end return p c7307fa3959d308a2dd7fd2f5009c1ce6db3d122 Module:InfoboxImage 828 416 832 2022-03-13T19:18:18Z wikipedia>WOSlinker 0 add class param from sandbox as per edit request Scribunto text/plain -- Inputs: -- image - Can either be a bare filename (with or without the File:/Image: prefix) or a fully formatted image link -- page - page to display for multipage images (DjVu) -- size - size to display the image -- maxsize - maximum size for image -- sizedefault - default size to display the image if size param is blank -- alt - alt text for image -- title - title text for image -- border - set to yes if border -- center - set to yes, if the image has to be centered -- upright - upright image param -- suppressplaceholder - if yes then checks to see if image is a placeholder and suppresses it -- link - page to visit when clicking on image -- class - HTML classes to add to the image -- Outputs: -- Formatted image. -- More details available at the "Module:InfoboxImage/doc" page local i = {}; local placeholder_image = { "Blue - Replace this image female.svg", "Blue - Replace this image male.svg", "Female no free image yet.png", "Flag of None (square).svg", "Flag of None.svg", "Flag of.svg", "Green - Replace this image female.svg", "Green - Replace this image male.svg", "Image is needed female.svg", "Image is needed male.svg", "Location map of None.svg", "Male no free image yet.png", "Missing flag.png", "No flag.svg", "No free portrait.svg", "No portrait (female).svg", "No portrait (male).svg", "Red - Replace this image female.svg", "Red - Replace this image male.svg", "Replace this image female (blue).svg", "Replace this image female.svg", "Replace this image male (blue).svg", "Replace this image male.svg", "Silver - Replace this image female.svg", "Silver - Replace this image male.svg", "Replace this image.svg", "Cricket no pic.png", "CarersLogo.gif", "Diagram Needed.svg", "Example.jpg", "Image placeholder.png", "No male portrait.svg", "Nocover-upload.png", "NoDVDcover copy.png", "Noribbon.svg", "No portrait-BFD-test.svg", "Placeholder barnstar ribbon.png", "Project Trains no image.png", "Image-request.png", "Sin bandera.svg", "Sin escudo.svg", "Replace this image - temple.png", "Replace this image butterfly.png", "Replace this image.svg", "Replace this image1.svg", "Resolution angle.png", "Image-No portrait-text-BFD-test.svg", "Insert image here.svg", "No image available.png", "NO IMAGE YET square.png", "NO IMAGE YET.png", "No Photo Available.svg", "No Screenshot.svg", "No-image-available.jpg", "Null.png", "PictureNeeded.gif", "Place holder.jpg", "Unbenannt.JPG", "UploadACopyrightFreeImage.svg", "UploadAnImage.gif", "UploadAnImage.svg", "UploadAnImageShort.svg", "CarersLogo.gif", "Diagram Needed.svg", "No male portrait.svg", "NoDVDcover copy.png", "Placeholder barnstar ribbon.png", "Project Trains no image.png", "Image-request.png", "Noimage.gif", } function i.IsPlaceholder(image) -- change underscores to spaces image = mw.ustring.gsub(image, "_", " "); assert(image ~= nil, 'mw.ustring.gsub(image, "_", " ") must not return nil') -- if image starts with [[ then remove that and anything after | if mw.ustring.sub(image,1,2) == "[[" then image = mw.ustring.sub(image,3); image = mw.ustring.gsub(image, "([^|]*)|.*", "%1"); assert(image ~= nil, 'mw.ustring.gsub(image, "([^|]*)|.*", "%1") must not return nil') end -- Trim spaces image = mw.ustring.gsub(image, '^[ ]*(.-)[ ]*$', '%1'); assert(image ~= nil, "mw.ustring.gsub(image, '^[ ]*(.-)[ ]*$', '%1') must not return nil") -- remove prefix if exists local allNames = mw.site.namespaces[6].aliases allNames[#allNames + 1] = mw.site.namespaces[6].name allNames[#allNames + 1] = mw.site.namespaces[6].canonicalName for i, name in ipairs(allNames) do if mw.ustring.lower(mw.ustring.sub(image, 1, mw.ustring.len(name) + 1)) == mw.ustring.lower(name .. ":") then image = mw.ustring.sub(image, mw.ustring.len(name) + 2); break end end -- Trim spaces image = mw.ustring.gsub(image, '^[ ]*(.-)[ ]*$', '%1'); -- capitalise first letter image = mw.ustring.upper(mw.ustring.sub(image,1,1)) .. mw.ustring.sub(image,2); for i,j in pairs(placeholder_image) do if image == j then return true end end return false end function i.InfoboxImage(frame) local image = frame.args["image"]; if image == "" or image == nil then return ""; end if image == "&nbsp;" then return image; end if frame.args["suppressplaceholder"] ~= "no" then if i.IsPlaceholder(image) == true then return ""; end end if mw.ustring.lower(mw.ustring.sub(image,1,5)) == "http:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,6)) == "[http:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,7)) == "[[http:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,6)) == "https:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,7)) == "[https:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,8)) == "[[https:" then return ""; end if mw.ustring.sub(image,1,2) == "[[" then -- search for thumbnail images and add to tracking cat if found local cat = ""; if mw.title.getCurrentTitle().namespace == 0 and (mw.ustring.find(image, "|%s*thumb%s*[|%]]") or mw.ustring.find(image, "|%s*thumbnail%s*[|%]]")) then cat = "[[Category:Pages using infoboxes with thumbnail images]]"; end return image .. cat; elseif mw.ustring.sub(image,1,2) == "{{" and mw.ustring.sub(image,1,3) ~= "{{{" then return image; elseif mw.ustring.sub(image,1,1) == "<" then return image; elseif mw.ustring.sub(image,1,5) == mw.ustring.char(127).."UNIQ" then -- Found strip marker at begining, so pass don't process at all return image; elseif mw.ustring.sub(image,4,9) == "`UNIQ-" then -- Found strip marker at begining, so pass don't process at all return image; else local result = ""; local page = frame.args["page"]; local size = frame.args["size"]; local maxsize = frame.args["maxsize"]; local sizedefault = frame.args["sizedefault"]; local alt = frame.args["alt"]; local link = frame.args["link"]; local title = frame.args["title"]; local border = frame.args["border"]; local upright = frame.args["upright"] or ""; local thumbtime = frame.args["thumbtime"] or ""; local center = frame.args["center"]; local class = frame.args["class"]; -- remove prefix if exists local allNames = mw.site.namespaces[6].aliases allNames[#allNames + 1] = mw.site.namespaces[6].name allNames[#allNames + 1] = mw.site.namespaces[6].canonicalName for i, name in ipairs(allNames) do if mw.ustring.lower(mw.ustring.sub(image, 1, mw.ustring.len(name) + 1)) == mw.ustring.lower(name .. ":") then image = mw.ustring.sub(image, mw.ustring.len(name) + 2); break end end if maxsize ~= "" and maxsize ~= nil then -- if no sizedefault then set to maxsize if sizedefault == "" or sizedefault == nil then sizedefault = maxsize end -- check to see if size bigger than maxsize if size ~= "" and size ~= nil then local sizenumber = tonumber(mw.ustring.match(size,"%d*")) or 0; local maxsizenumber = tonumber(mw.ustring.match(maxsize,"%d*")) or 0; if sizenumber>maxsizenumber and maxsizenumber>0 then size = maxsize; end end end -- add px to size if just a number if (tonumber(size) or 0) > 0 then size = size .. "px"; end -- add px to sizedefault if just a number if (tonumber(sizedefault) or 0) > 0 then sizedefault = sizedefault .. "px"; end result = "[[File:" .. image; if page ~= "" and page ~= nil then result = result .. "|page=" .. page; end if size ~= "" and size ~= nil then result = result .. "|" .. size; elseif sizedefault ~= "" and sizedefault ~= nil then result = result .. "|" .. sizedefault; else result = result .. "|frameless"; end if center == "yes" then result = result .. "|center" end if alt ~= "" and alt ~= nil then result = result .. "|alt=" .. alt; end if link ~= "" and link ~= nil then result = result .. "|link=" .. link; end if border == "yes" then result = result .. "|border"; end if upright == "yes" then result = result .. "|upright"; elseif upright ~= "" then result = result .. "|upright=" .. upright; end if thumbtime ~= "" then result = result .. "|thumbtime=" .. thumbtime; end if class ~= nil and class ~= "" then result = result .. "|class=" .. class; end -- if alt value is a keyword then do not use as a description if alt == "thumbnail" or alt == "thumb" or alt == "frameless" or alt == "left" or alt == "center" or alt == "right" or alt == "upright" or alt == "border" or mw.ustring.match(alt or "", '^[0-9]*px$', 1) ~= nil then alt = nil; end if title ~= "" and title ~= nil then -- does title param contain any templatestyles? If yes then set to blank. if mw.ustring.match(frame:preprocess(title), 'UNIQ%-%-templatestyles', 1) ~= nil then title = nil; end end if title ~= "" and title ~= nil then result = result .. "|" .. title; end result = result .. "]]"; return result; end end return i; 0ee5fe75ba239fc5c9cedc81ca11bdc0be068542 Template:Documentation 10 326 647 2022-03-29T02:14:34Z wikipedia>Bsherr 0 consistent with new substitution template format wikitext text/x-wiki {{#invoke:documentation|main|_content={{ {{#invoke:documentation|contentTitle}}}}}}<noinclude> <!-- Add categories to the /doc subpage --> </noinclude> 9e62b964e96c4e3d478edecbfcb3c0338ae8a276 Template:Wargaming Chicago-Baltimore 10 477 954 2022-04-17T22:49:47Z wikipedia>Bertaut 0 [[WP:AES|←]]Created page with '{{Navbox | name = Wargaming Chicago-Baltimore | title = [[Wargaming Chicago-Baltimore]] | state = {{{state|autocollapse}}} | listclass = hlist | group1 = As Meyer/Glass Interactive | list1 = * ''[[Axis & Allies (1998 video game)|Axis & Allies]]'' * ''[[Avalon Hill's Diplomacy]]'' * ''[[Battleship: Surface Thunder]]'' | group2 = As Day 1 Studios | list2 = * ''[[MechAssault]]'' * ''[[MechAssault 2: Lone Wolf]]'' * ''[[Fracture (video game)|Fracture]]'' * '...' wikitext text/x-wiki {{Navbox | name = Wargaming Chicago-Baltimore | title = [[Wargaming Chicago-Baltimore]] | state = {{{state|autocollapse}}} | listclass = hlist | group1 = As Meyer/Glass Interactive | list1 = * ''[[Axis & Allies (1998 video game)|Axis & Allies]]'' * ''[[Avalon Hill's Diplomacy]]'' * ''[[Battleship: Surface Thunder]]'' | group2 = As Day 1 Studios | list2 = * ''[[MechAssault]]'' * ''[[MechAssault 2: Lone Wolf]]'' * ''[[Fracture (video game)|Fracture]]'' * ''[[F.E.A.R. 3]]'' | group3 = As Wargaming Chicago-Baltimore | list3 = * ''[[World of Tanks]]'' }} <noinclude> {{collapsible option}} {{Documentation}} [[Category:Video game navigational boxes by company]] </noinclude> 2698d524b1473072d3cde37ae1f9e7992ae6b32f Module:Ml 828 518 1044 2022-04-22T11:31:32Z string2>MSGJ 0 MSGJ moved page [[Template:Ml]] to [[Template:Module link]]: clearer name wikitext text/x-wiki #REDIRECT [[Template:Module link]] {{Redirect category shell| {{R from move}} }} 55769c1a9d3b6c6828298b7a402d8e8506532cdd Template:Multiple issues/styles.css 10 393 786 2022-05-07T20:39:15Z wikipedia>Izno 0 add these other inline styles text text/plain /* {{pp|small=y}} */ .multiple-issues-text { width: 95%; margin: 0.2em 0; } .multiple-issues-text > .mw-collapsible-content { margin-top: 0.3em; } /* Remove borders, backgrounds, padding, etc. */ .compact-ambox .ambox { border: none; border-collapse: collapse; background-color: transparent; margin: 0 0 0 1.6em !important; padding: 0 !important; width: auto; display: block; } body.mediawiki .compact-ambox .ambox.mbox-small-left { font-size: 100%; width: auto; margin: 0; } /* Style the text cell as a list item and remove its padding */ .compact-ambox .ambox .mbox-text { padding: 0 !important; margin: 0 !important; } .compact-ambox .ambox .mbox-text-span { display: list-item; line-height: 1.5em; list-style-type: disc; } /* Hide the images */ .compact-ambox .ambox .mbox-image, .compact-ambox .ambox .mbox-imageright, .compact-ambox .ambox .mbox-empty-cell, /* Allow for hiding text in compact form */ .compact-ambox .hide-when-compact { display: none; } e90883916010fd38cd4f9c7e10c4a01908c965cb Template:Multiple issues 10 392 784 2022-05-07T20:41:52Z wikipedia>Izno 0 add templatestyles wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||$B= {{Ambox |name = Multiple issues |templatestyles = Multiple issues/styles.css |doc = no |subst = <includeonly>{{subst:substcheck}}</includeonly> |class = ambox-multiple_issues compact-ambox |type = content |removalnotice = yes <!-- as of 2016-06, param does not work on this template --> |cat=Articles with multiple maintenance issues |text = <div class="multiple-issues-text {{#if:{{{1|}}}|mw-collapsible {{#ifeq:{{{collapsed}}}|yes|mw-collapsed}}}}"><!-- -->'''This {{#if:{{{section|}}}|section|article}} has multiple issues.''' Please help '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} improve it]''' or discuss these issues on the '''[[{{TALKPAGENAME}}|talk page]]'''. <small>''([[Help:Maintenance template removal|Learn how and when to remove these template messages]])''</small> {{#if:{{{1|}}} | <div class="mw-collapsible-content"> {{#invoke:String|replace|source={{{1|}}}|pattern=style="display: none"|replace=|count=}}<!--remove style="display: none", to support display of {{orphan}} messages in {{multiple issues}}--> </div> | <includeonly>{{error|No issues specified. Please specify issues, or remove this template.}}</includeonly> }} </div> {{#if:{{{2|}}}|[[Category:Pages using multiple issues with unknown parameters|§{{PAGENAME}}]]}} }}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using multiple issues with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Multiple issues]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | 2 | collapsed | section }} }}<noinclude> {{Documentation}} </noinclude> 901c03957b27f754d3caf69d5c74740f793c9a03 Module:String2 828 495 990 2022-05-14T12:03:50Z string2>GKFX 0 Copy changes by [[User:Tholme]] from [[Module:String2/sandbox]] per edit request. Scribunto text/plain local p = {} p.trim = function(frame) return mw.text.trim(frame.args[1] or "") end p.sentence = function (frame) -- {{lc:}} is strip-marker safe, string.lower is not. frame.args[1] = frame:callParserFunction('lc', frame.args[1]) return p.ucfirst(frame) end p.ucfirst = function (frame ) local s = mw.text.trim( frame.args[1] or "" ) local s1 = "" -- if it's a list chop off and (store as s1) everything up to the first <li> local lipos = mw.ustring.find(s, "<li>" ) if lipos then s1 = mw.ustring.sub(s, 1, lipos + 3) s = mw.ustring.sub(s, lipos + 4) end -- s1 is either "" or the first part of the list markup, so we can continue -- and prepend s1 to the returned string local letterpos if mw.ustring.find(s, "^%[%[[^|]+|[^%]]+%]%]") then -- this is a piped wikilink, so we capitalise the text, not the pipe local _ _, letterpos = mw.ustring.find(s, "|%W*%w") -- find the first letter after the pipe else letterpos = mw.ustring.find(s, '%w') end if letterpos then local first = mw.ustring.sub(s, 1, letterpos - 1) local letter = mw.ustring.sub(s, letterpos, letterpos) local rest = mw.ustring.sub(s, letterpos + 1) return s1 .. first .. mw.ustring.upper(letter) .. rest else return s1 .. s end end p.title = function (frame ) -- http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html -- recommended by The U.S. Government Printing Office Style Manual: -- "Capitalize all words in titles of publications and documents, -- except a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor." local alwayslower = {['a'] = 1, ['an'] = 1, ['the'] = 1, ['and'] = 1, ['but'] = 1, ['or'] = 1, ['for'] = 1, ['nor'] = 1, ['on'] = 1, ['in'] = 1, ['at'] = 1, ['to'] = 1, ['from'] = 1, ['by'] = 1, ['of'] = 1, ['up'] = 1 } local res = '' local s = mw.text.trim( frame.args[1] or "" ) local words = mw.text.split( s, " ") for i, s in ipairs(words) do -- {{lc:}} is strip-marker safe, string.lower is not. s = frame:callParserFunction('lc', s) if i == 1 or alwayslower[s] ~= 1 then s = mw.getContentLanguage():ucfirst(s) end words[i] = s end return table.concat(words, " ") end -- findlast finds the last item in a list -- the first unnamed parameter is the list -- the second, optional unnamed parameter is the list separator (default = comma space) -- returns the whole list if separator not found p.findlast = function(frame) local s = mw.text.trim( frame.args[1] or "" ) local sep = frame.args[2] or "" if sep == "" then sep = ", " end local pattern = ".*" .. sep .. "(.*)" local a, b, last = s:find(pattern) if a then return last else return s end end -- stripZeros finds the first number and strips leading zeros (apart from units) -- e.g "0940" -> "940"; "Year: 0023" -> "Year: 23"; "00.12" -> "0.12" p.stripZeros = function(frame) local s = mw.text.trim(frame.args[1] or "") local n = tonumber( string.match( s, "%d+" ) ) or "" s = string.gsub( s, "%d+", n, 1 ) return s end -- nowiki ensures that a string of text is treated by the MediaWiki software as just a string -- it takes an unnamed parameter and trims whitespace, then removes any wikicode p.nowiki = function(frame) local str = mw.text.trim(frame.args[1] or "") return mw.text.nowiki(str) end -- split splits text at boundaries specified by separator -- and returns the chunk for the index idx (starting at 1) -- #invoke:String2 |split |text |separator |index |true/false -- #invoke:String2 |split |txt=text |sep=separator |idx=index |plain=true/false -- if plain is false/no/0 then separator is treated as a Lua pattern - defaults to plain=true p.split = function(frame) local args = frame.args if not(args[1] or args.txt) then args = frame:getParent().args end local txt = args[1] or args.txt or "" if txt == "" then return nil end local sep = (args[2] or args.sep or ""):gsub('"', '') local idx = tonumber(args[3] or args.idx) or 1 local plain = (args[4] or args.plain or "true"):sub(1,1) plain = (plain ~= "f" and plain ~= "n" and plain ~= "0") local splittbl = mw.text.split( txt, sep, plain ) if idx < 0 then idx = #splittbl + idx + 1 end return splittbl[idx] end -- val2percent scans through a string, passed as either the first unnamed parameter or |txt= -- it converts each number it finds into a percentage and returns the resultant string. p.val2percent = function(frame) local args = frame.args if not(args[1] or args.txt) then args = frame:getParent().args end local txt = mw.text.trim(args[1] or args.txt or "") if txt == "" then return nil end local function v2p (x) x = (tonumber(x) or 0) * 100 if x == math.floor(x) then x = math.floor(x) end return x .. "%" end txt = txt:gsub("%d[%d%.]*", v2p) -- store just the string return txt end -- one2a scans through a string, passed as either the first unnamed parameter or |txt= -- it converts each occurrence of 'one ' into either 'a ' or 'an ' and returns the resultant string. p.one2a = function(frame) local args = frame.args if not(args[1] or args.txt) then args = frame:getParent().args end local txt = mw.text.trim(args[1] or args.txt or "") if txt == "" then return nil end txt = txt:gsub(" one ", " a "):gsub("^one", "a"):gsub("One ", "A "):gsub("a ([aeiou])", "an %1"):gsub("A ([aeiou])", "An %1") return txt end -- findpagetext returns the position of a piece of text in a page -- First positional parameter or |text is the search text -- Optional parameter |title is the page title, defaults to current page -- Optional parameter |plain is either true for plain search (default) or false for Lua pattern search -- Optional parameter |nomatch is the return value when no match is found; default is nil p._findpagetext = function(args) -- process parameters local nomatch = args.nomatch or "" if nomatch == "" then nomatch = nil end -- local text = mw.text.trim(args[1] or args.text or "") if text == "" then return nil end -- local title = args.title or "" local titleobj if title == "" then titleobj = mw.title.getCurrentTitle() else titleobj = mw.title.new(title) end -- local plain = args.plain or "" if plain:sub(1, 1) == "f" then plain = false else plain = true end -- get the page content and look for 'text' - return position or nomatch local content = titleobj and titleobj:getContent() return content and mw.ustring.find(content, text, 1, plain) or nomatch end p.findpagetext = function(frame) local args = frame.args local pargs = frame:getParent().args for k, v in pairs(pargs) do args[k] = v end if not (args[1] or args.text) then return nil end -- just the first value return (p._findpagetext(args)) end -- returns the decoded url. Inverse of parser function {{urlencode:val|TYPE}} -- Type is: -- QUERY decodes + to space (default) -- PATH does no extra decoding -- WIKI decodes _ to space p._urldecode = function(url, type) url = url or "" type = (type == "PATH" or type == "WIKI") and type return mw.uri.decode( url, type ) end -- {{#invoke:String2|urldecode|url=url|type=type}} p.urldecode = function(frame) return mw.uri.decode( frame.args.url, frame.args.type ) end -- what follows was merged from Module:StringFunc -- helper functions p._GetParameters = require('Module:GetParameters') -- Argument list helper function, as per Module:String p._getParameters = p._GetParameters.getParameters -- Escape Pattern helper function so that all characters are treated as plain text, as per Module:String function p._escapePattern( pattern_str) return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ) end -- Helper Function to interpret boolean strings, as per Module:String p._getBoolean = p._GetParameters.getBoolean --[[ Strip This function Strips characters from string Usage: {{#invoke:String2|strip|source_string|characters_to_strip|plain_flag}} Parameters source: The string to strip chars: The pattern or list of characters to strip from string, replaced with '' plain: A flag indicating that the chars should be understood as plain text. defaults to true. Leading and trailing whitespace is also automatically stripped from the string. ]] function p.strip( frame ) local new_args = p._getParameters( frame.args, {'source', 'chars', 'plain'} ) local source_str = new_args['source'] or '' local chars = new_args['chars'] or '' or 'characters' source_str = mw.text.trim(source_str) if source_str == '' or chars == '' then return source_str end local l_plain = p._getBoolean( new_args['plain'] or true ) if l_plain then chars = p._escapePattern( chars ) end local result result = mw.ustring.gsub(source_str, "["..chars.."]", '') return result end --[[ Match any Returns the index of the first given pattern to match the input. Patterns must be consecutively numbered. Returns the empty string if nothing matches for use in {{#if:}} Usage: {{#invoke:String2|matchAll|source=123 abc|456|abc}} returns '2'. Parameters: source: the string to search plain: A flag indicating that the patterns should be understood as plain text. defaults to true. 1, 2, 3, ...: the patterns to search for ]] function p.matchAny(frame) local source_str = frame.args['source'] or error('The source parameter is mandatory.') local l_plain = p._getBoolean( frame.args['plain'] or true ) for i = 1, math.huge do local pattern = frame.args[i] if not pattern then return '' end if mw.ustring.find(source_str, pattern, 1, l_plain) then return tostring(i) end end end --[[--------------------------< H Y P H E N _ T O _ D A S H >-------------------------------------------------- Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are returned unmodified. These forms are modified: letter - letter (A - B) digit - digit (4-5) digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d) any other forms are returned unmodified. str may be a comma- or semicolon-separated list ]] function p.hyphen_to_dash( str, spacing ) if (str == nil or str == '') then return str end local accept str = mw.text.decode(str, true ) -- replace html entities with their characters; semicolon mucks up the text.split local out = {} local list = mw.text.split (str, '%s*[,;]%s*') -- split str at comma or semicolon separators if there are any for _, item in ipairs (list) do -- for each item in the list item = mw.text.trim(item) -- trim whitespace item, accept = item:gsub ('^%(%((.+)%)%)$', '%1') if accept == 0 and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit) item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter) item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or -- digit separator digit hyphen digit separator digit item:match ('^%d+%s*%-%s*%d+$') or -- digit hyphen digit item:match ('^%a+%s*%-%s*%a+$') then -- letter hyphen letter item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2') -- replace hyphen, remove extraneous space characters else item = mw.ustring.gsub (item, '%s*[–—]%s*', '–') -- for endash or emdash separated ranges, replace em with en, remove extraneous whitespace end end table.insert (out, item) -- add the (possibly modified) item to the output table end local temp_str = table.concat (out, ',' .. spacing) -- concatenate the output table into a comma separated string temp_str, accept = temp_str:gsub ('^%(%((.+)%)%)$', '%1') -- remove accept-this-as-written markup when it wraps all of concatenated out if accept ~= 0 then temp_str = str:gsub ('^%(%((.+)%)%)$', '%1') -- when global markup removed, return original str; do it this way to suppress boolean second return value end return temp_str end function p.hyphen2dash( frame ) local str = frame.args[1] or '' local spacing = frame.args[2] or ' ' -- space is part of the standard separator for normal spacing (but in conjunction with templates r/rp/ran we may need a narrower spacing return p.hyphen_to_dash(str, spacing) end -- Similar to [[Module:String#endswith]] function p.startswith(frame) return (frame.args[1]:sub(1, frame.args[2]:len()) == frame.args[2]) and 'yes' or '' end return p 418f407ee2454811910e333a13523afc975fa40c Module:Module rating 828 369 732 2022-06-03T15:10:23Z wikipedia>The Anome 0 Reverted edits by [[Special:Contribs/Dawn PScLim|Dawn PScLim]] ([[User talk:Dawn PScLim|talk]]) to last version by Alexis Jazz wikitext text/x-wiki <includeonly>{{#ifeq:{{SUBPAGENAME}}|doc|<!--do not show protection level of the module on the doc page, use the second and optionally third parameter if the doc page is also protected -->{{#if:{{{2|}}}|{{Pp|{{{2}}}|action={{{3|}}}}}}}|{{Module other|{{ombox | type = notice | image = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = [[File:Ambox warning blue construction.svg|40x40px|link=|alt=Pre-alpha]] | alpha | a = [[File:Alpha lowercase.svg|26x26px|link=|alt=Alpha]] | beta | b = [[File:Greek lc beta.svg|40x40px|link=|alt=Beta]] | release | r | general | g = [[File:Green check.svg|40x40px|link=|alt=Ready for use]] | protected | protect | p = [[File:{{#switch:{{#invoke:Effective protection level|edit|{{#switch:{{SUBPAGENAME}}|doc|sandbox={{FULLBASEPAGENAME}}|{{FULLPAGENAME}}}}}}|autoconfirmed=Semi|extendedconfirmed=Extended|accountcreator|templateeditor=Template|#default=Full}}-protection-shackle.svg|40x40px|link=|alt=Protected]] | semiprotected | semiprotect | semi =[[File:Semi-protection-shackle.svg|40x40px|link=|alt=Semi-protected]] }} | style = | textstyle = | text = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = This module is rated as [[:Category:Modules in pre-alpha development|pre-alpha]]. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in pre-alpha development|{{PAGENAME}}]] }} }} | alpha | a = This module is rated as [[:Category:Modules in alpha|alpha]]. It is ready for third-party input, and may be used on a few pages to see if problems arise, but should be watched. Suggestions for new features or changes in their input and output mechanisms are welcome.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in alpha|{{PAGENAME}}]] }} }} | beta | b = This module is rated as [[:Category:Modules in beta|beta]], and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in beta|{{PAGENAME}}]] }} }} | release | r | general | g = This module is rated as [[:Category:Modules for general use|ready for general use]]. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by [[Wikipedia:Template sandbox and test cases|sandbox testing]] rather than repeated trial-and-error editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules for general use|{{PAGENAME}}]] }} }} | protected | protect | p = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[Wikipedia:Protection policy|protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | semiprotected | semiprotect | semi = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[WP:SEMI|semi-protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | #default = {{error|Module rating is invalid or not specified.}} }} }}|{{error|Error: {{tl|Module rating}} must be placed in the Module namespace.}} [[Category:Pages with templates in the wrong namespace]]|demospace={{{demospace|<noinclude>module</noinclude>}}}}}}}</includeonly><noinclude> {{module rating|release|nocat=true|demospace=module}} {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go in Wikidata. --> </noinclude> bbd244b3ea2e13ec4c1c810ae44f2f3789a93efc Template:Module rating 10 379 754 2022-06-03T15:10:23Z wikipedia>The Anome 0 Reverted edits by [[Special:Contribs/Dawn PScLim|Dawn PScLim]] ([[User talk:Dawn PScLim|talk]]) to last version by Alexis Jazz wikitext text/x-wiki <includeonly>{{#ifeq:{{SUBPAGENAME}}|doc|<!--do not show protection level of the module on the doc page, use the second and optionally third parameter if the doc page is also protected -->{{#if:{{{2|}}}|{{Pp|{{{2}}}|action={{{3|}}}}}}}|{{Module other|{{ombox | type = notice | image = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = [[File:Ambox warning blue construction.svg|40x40px|link=|alt=Pre-alpha]] | alpha | a = [[File:Alpha lowercase.svg|26x26px|link=|alt=Alpha]] | beta | b = [[File:Greek lc beta.svg|40x40px|link=|alt=Beta]] | release | r | general | g = [[File:Green check.svg|40x40px|link=|alt=Ready for use]] | protected | protect | p = [[File:{{#switch:{{#invoke:Effective protection level|edit|{{#switch:{{SUBPAGENAME}}|doc|sandbox={{FULLBASEPAGENAME}}|{{FULLPAGENAME}}}}}}|autoconfirmed=Semi|extendedconfirmed=Extended|accountcreator|templateeditor=Template|#default=Full}}-protection-shackle.svg|40x40px|link=|alt=Protected]] | semiprotected | semiprotect | semi =[[File:Semi-protection-shackle.svg|40x40px|link=|alt=Semi-protected]] }} | style = | textstyle = | text = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = This module is rated as [[:Category:Modules in pre-alpha development|pre-alpha]]. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in pre-alpha development|{{PAGENAME}}]] }} }} | alpha | a = This module is rated as [[:Category:Modules in alpha|alpha]]. It is ready for third-party input, and may be used on a few pages to see if problems arise, but should be watched. Suggestions for new features or changes in their input and output mechanisms are welcome.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in alpha|{{PAGENAME}}]] }} }} | beta | b = This module is rated as [[:Category:Modules in beta|beta]], and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in beta|{{PAGENAME}}]] }} }} | release | r | general | g = This module is rated as [[:Category:Modules for general use|ready for general use]]. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by [[Wikipedia:Template sandbox and test cases|sandbox testing]] rather than repeated trial-and-error editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules for general use|{{PAGENAME}}]] }} }} | protected | protect | p = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[Wikipedia:Protection policy|protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | semiprotected | semiprotect | semi = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[WP:SEMI|semi-protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | #default = {{error|Module rating is invalid or not specified.}} }} }}|{{error|Error: {{tl|Module rating}} must be placed in the Module namespace.}} [[Category:Pages with templates in the wrong namespace]]|demospace={{{demospace|<noinclude>module</noinclude>}}}}}}}</includeonly><noinclude> {{module rating|release|nocat=true|demospace=module}} {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go in Wikidata. --> </noinclude> bbd244b3ea2e13ec4c1c810ae44f2f3789a93efc Template:Reflist 10 423 846 2022-06-11T17:42:16Z wikipedia>Izno 0 correct my error per [[Template talk:Reflist#liststyle and group]] wikitext text/x-wiki <templatestyles src="Reflist/styles.css" /><div class="reflist <!-- -->{{#if:{{{1|}}}{{{colwidth|}}}|reflist-columns references-column-width}} <!-- -->{{#switch:{{{liststyle|{{{group|}}}}}}|upper-alpha|upper-roman|lower-alpha|lower-greek|lower-roman=reflist-{{{liststyle|{{{group}}}}}}}} <!-- -->{{#if:{{{1|}}}|{{#iferror:{{#ifexpr: {{{1|1}}} > 1 }}||{{#switch:{{{1|}}}|1=|2=reflist-columns-2|#default=reflist-columns-3}} }}}}" <!-- end class -->{{#if: {{{1|}}}<!-- start style --> | {{#iferror: {{#ifexpr: {{{1|1}}} > 1 }} |style="column-width: {{{1}}};"}} | {{#if: {{{colwidth|}}}|style="column-width: {{{colwidth}}};"}} }}> {{#tag:references|{{{refs|}}}|group={{{group|}}}|responsive={{#if:{{{1|}}}{{{colwidth|}}}|0|1}}}}</div>{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using reflist with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Reflist]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | colwidth | group | liststyle | refs }}<noinclude> {{Documentation}} </noinclude> 8c65cc88272db6c0f5cf2b49f84d3e460e60ee5f Template:Clear 10 315 625 2022-06-13T15:31:11Z wikipedia>Xaosflux 0 Changed protection settings for "[[Template:Clear]]": [[WP:High-risk templates|Highly visible template]]: 3MM+ uses ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite)) wikitext text/x-wiki <div style="clear:{{{1|both}}};"></div><noinclude> {{documentation}} </noinclude> 38bab3e3d7fbd3d6800d46556e60bc6bac494d72 Module:Uses TemplateStyles/config 828 372 740 2022-06-16T15:10:06Z wikipedia>Pppery 0 Matching reality rather than 2018 me's wishful thinking Scribunto text/plain local cfg = {} -- Don’t touch this line. -- Subpage blacklist: these subpages will not be categorized (except for the -- error category, which is always added if there is an error). -- For example “Template:Foo/doc” matches the `doc = true` rule, so it will have -- no categories. “Template:Foo” and “Template:Foo/documentation” match no rules, -- so they *will* have categories. All rules should be in the -- ['<subpage name>'] = true, -- format. cfg['subpage_blacklist'] = { ['doc'] = true, ['sandbox'] = true, ['sandbox2'] = true, ['testcases'] = true, } -- Sandbox title: if the stylesheet’s title is <template>/<stylesheet>.css, the -- stylesheet’s sandbox is expected to be at <template>/<sandbox_title>/<stylesheet>.css -- Set to nil to disable sandbox links. cfg['sandbox_title'] = 'sandbox' -- Error category: this category is added if the module call contains errors -- (e.g. no stylesheet listed). A category name without namespace, or nil -- to disable categorization (not recommended). cfg['error_category'] = 'Uses TemplateStyles templates with errors' -- Default category: this category is added if no custom category is specified -- in module/template call. A category name without namespace, or nil -- to disable categorization. cfg['default_category'] = 'Templates using TemplateStyles' -- Protection conflict category: this category is added if the protection level -- of any stylesheet is lower than the protection level of the template. A category name -- without namespace, or nil to disable categorization (not recommended). cfg['protection_conflict_category'] = 'Templates using TemplateStyles with a different protection level' -- Hierarchy of protection levels, used to determine whether one protection level is lower -- than another and thus should populate protection_conflict_category. No protection is treated as zero cfg['protection_hierarchy'] = { autoconfirmed = 1, extendedconfirmed = 2, templateeditor = 3, sysop = 4 } -- Padlock pattern: Lua pattern to search on protected stylesheets for, or nil -- to disable padlock check. cfg['padlock_pattern'] = '{{pp-' -- Missing padlock category: this category is added if a protected stylesheet -- doesn’t contain any padlock template (specified by the above Lua pattern). -- A category name without namespace (no nil allowed) if the pattern is not nil, -- unused (and thus may be nil) otherwise. cfg['missing_padlock_category'] = 'Templates using TemplateStyles without padlocks' return cfg -- Don’t touch this line. 58e7a37c44f6ea3f6b8af54a559d696cc7256493 Module:Uses TemplateStyles 828 371 738 2022-06-16T15:13:38Z wikipedia>Pppery 0 Matching reality rather than 2018 me's wishful thinking Scribunto text/plain local yesno = require('Module:Yesno') local mList = require('Module:List') local mTableTools = require('Module:TableTools') local mMessageBox = require('Module:Message box') local TNT = require('Module:TNT') local p = {} local function format(msg, ...) return TNT.format('I18n/Uses TemplateStyles', msg, ...) end local function getConfig() return mw.loadData('Module:Uses TemplateStyles/config') end local function renderBox(tStyles) local boxArgs = { type = 'notice', small = true, image = string.format('[[File:Farm-Fresh css add.svg|32px|alt=%s]]', format('logo-alt')) } if #tStyles < 1 then boxArgs.text = string.format('<strong class="error">%s</strong>', format('error-emptylist')) else local cfg = getConfig() local tStylesLinks = {} for i, ts in ipairs(tStyles) do local link = string.format('[[:%s]]', ts) local sandboxLink = nil local tsTitle = mw.title.new(ts) if tsTitle and cfg['sandbox_title'] then local tsSandboxTitle = mw.title.new(string.format( '%s:%s/%s/%s', tsTitle.nsText, tsTitle.baseText, cfg['sandbox_title'], tsTitle.subpageText)) if tsSandboxTitle and tsSandboxTitle.exists then sandboxLink = format('sandboxlink', link, ':' .. tsSandboxTitle.prefixedText) end end tStylesLinks[i] = sandboxLink or link end local tStylesList = mList.makeList('bulleted', tStylesLinks) boxArgs.text = format( mw.title.getCurrentTitle():inNamespaces(828,829) and 'header-module' or 'header-template') .. '\n' .. tStylesList end return mMessageBox.main('mbox', boxArgs) end local function renderTrackingCategories(args, tStyles, titleObj) if yesno(args.nocat) then return '' end local cfg = getConfig() local cats = {} -- Error category if #tStyles < 1 and cfg['error_category'] then cats[#cats + 1] = cfg['error_category'] end -- TemplateStyles category titleObj = titleObj or mw.title.getCurrentTitle() if (titleObj.namespace == 10 or titleObj.namespace == 828) and not cfg['subpage_blacklist'][titleObj.subpageText] then local category = args.category or cfg['default_category'] if category then cats[#cats + 1] = category end if not yesno(args.noprotcat) and (cfg['protection_conflict_category'] or cfg['padlock_pattern']) then local currentProt = titleObj.protectionLevels["edit"] and titleObj.protectionLevels["edit"][1] or nil local addedLevelCat = false local addedPadlockCat = false for i, ts in ipairs(tStyles) do local tsTitleObj = mw.title.new(ts) local tsProt = tsTitleObj.protectionLevels["edit"] and tsTitleObj.protectionLevels["edit"][1] or nil if cfg['padlock_pattern'] and tsProt and not addedPadlockCat then local content = tsTitleObj:getContent() if not content:find(cfg['padlock_pattern']) then cats[#cats + 1] = cfg['missing_padlock_category'] addedPadlockCat = true end end if cfg['protection_conflict_category'] and currentProt and tsProt ~= currentProt and not addedLevelCat then currentProt = cfg['protection_hierarchy'][currentProt] or 0 tsProt = cfg['protection_hierarchy'][tsProt] or 0 if tsProt < currentProt then addedLevelCat = true cats[#cats + 1] = cfg['protection_conflict_category'] end end end end end for i, cat in ipairs(cats) do cats[i] = string.format('[[Category:%s]]', cat) end return table.concat(cats) end function p._main(args, cfg) local tStyles = mTableTools.compressSparseArray(args) local box = renderBox(tStyles) local trackingCategories = renderTrackingCategories(args, tStyles) return box .. trackingCategories end function p.main(frame) local origArgs = frame:getParent().args local args = {} for k, v in pairs(origArgs) do v = v:match('^%s*(.-)%s*$') if v ~= '' then args[k] = v end end return p._main(args) end return p 71ca57c37849f38e3c5ee30061bdae730963e48e Module:Message box/configuration 828 350 694 2022-07-11T18:19:26Z wikipedia>Izno 0 add templatestyles, remove a variable or two as a result Scribunto text/plain -------------------------------------------------------------------------------- -- Message box configuration -- -- -- -- This module contains configuration data for [[Module:Message box]]. -- -------------------------------------------------------------------------------- return { ambox = { types = { speedy = { class = 'ambox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'ambox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'ambox-content', image = 'Ambox important.svg' }, style = { class = 'ambox-style', image = 'Edit-clear.svg' }, move = { class = 'ambox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'ambox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'ambox-notice', image = 'Information icon4.svg' } }, default = 'notice', allowBlankParams = {'talk', 'sect', 'date', 'issue', 'fix', 'subst', 'hidden'}, allowSmall = true, smallParam = 'left', smallClass = 'mbox-small-left', substCheck = true, classes = {'metadata', 'ambox'}, imageEmptyCell = true, imageCheckBlank = true, imageSmallSize = '20x20px', imageCellDiv = true, useCollapsibleTextFields = true, imageRightNone = true, sectionDefault = 'article', allowMainspaceCategories = true, templateCategory = 'Article message templates', templateCategoryRequireName = true, templateErrorCategory = 'Article message templates with missing parameters', templateErrorParamsToCheck = {'issue', 'fix', 'subst'}, removalNotice = '<small>[[Help:Maintenance template removal|Learn how and when to remove this template message]]</small>', templatestyles = 'Module:Message box/ambox.css' }, cmbox = { types = { speedy = { class = 'cmbox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'cmbox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'cmbox-content', image = 'Ambox important.svg' }, style = { class = 'cmbox-style', image = 'Edit-clear.svg' }, move = { class = 'cmbox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'cmbox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'cmbox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'cmbox'}, imageEmptyCell = true, templatestyles = 'Module:Message box/cmbox.css' }, fmbox = { types = { warning = { class = 'fmbox-warning', image = 'Ambox warning pn.svg' }, editnotice = { class = 'fmbox-editnotice', image = 'Information icon4.svg' }, system = { class = 'fmbox-system', image = 'Information icon4.svg' } }, default = 'system', showInvalidTypeError = true, classes = {'fmbox'}, imageEmptyCell = false, imageRightNone = false, templatestyles = 'Module:Message box/fmbox.css' }, imbox = { types = { speedy = { class = 'imbox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'imbox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'imbox-content', image = 'Ambox important.svg' }, style = { class = 'imbox-style', image = 'Edit-clear.svg' }, move = { class = 'imbox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'imbox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, license = { class = 'imbox-license licensetpl', image = 'Imbox license.png' -- @todo We need an SVG version of this }, featured = { class = 'imbox-featured', image = 'Cscr-featured.svg' }, notice = { class = 'imbox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'imbox'}, imageEmptyCell = true, below = true, templateCategory = 'File message boxes', templatestyles = 'Module:Message box/imbox.css' }, ombox = { types = { speedy = { class = 'ombox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'ombox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'ombox-content', image = 'Ambox important.svg' }, style = { class = 'ombox-style', image = 'Edit-clear.svg' }, move = { class = 'ombox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'ombox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'ombox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'ombox'}, allowSmall = true, imageEmptyCell = true, imageRightNone = true, templatestyles = 'Module:Message box/ombox.css' }, tmbox = { types = { speedy = { class = 'tmbox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'tmbox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'tmbox-content', image = 'Ambox important.svg' }, style = { class = 'tmbox-style', image = 'Edit-clear.svg' }, move = { class = 'tmbox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'tmbox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'tmbox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'tmbox'}, allowSmall = true, imageRightNone = true, imageEmptyCell = true, templateCategory = 'Talk message boxes', templatestyles = 'Module:Message box/tmbox.css' } } b6f0151037e6867b577c8cca32ff297e48697a10 Template:Message box/ombox.css 10 340 675 2022-07-11T18:40:17Z wikipedia>Izno 0 and move mbox-small to 720px here as well text text/plain /* {{pp|small=y}} */ .ombox { margin: 4px 0; border-collapse: collapse; border: 1px solid #a2a9b1; /* Default "notice" gray */ background-color: #f8f9fa; box-sizing: border-box; } /* For the "small=yes" option. */ .ombox.mbox-small { font-size: 88%; line-height: 1.25em; } .ombox-speedy { border: 2px solid #b32424; /* Red */ background-color: #fee7e6; /* Pink */ } .ombox-delete { border: 2px solid #b32424; /* Red */ } .ombox-content { border: 1px solid #f28500; /* Orange */ } .ombox-style { border: 1px solid #fc3; /* Yellow */ } .ombox-move { border: 1px solid #9932cc; /* Purple */ } .ombox-protection { border: 2px solid #a2a9b1; /* Gray-gold */ } .ombox .mbox-text { border: none; /* @noflip */ padding: 0.25em 0.9em; width: 100%; } .ombox .mbox-image { border: none; /* @noflip */ padding: 2px 0 2px 0.9em; text-align: center; } .ombox .mbox-imageright { border: none; /* @noflip */ padding: 2px 0.9em 2px 0; text-align: center; } /* An empty narrow cell */ .ombox .mbox-empty-cell { border: none; padding: 0; width: 1px; } .ombox .mbox-invalid-type { text-align: center; } @media (min-width: 720px) { .ombox { margin: 4px 10%; } .ombox.mbox-small { /* @noflip */ clear: right; /* @noflip */ float: right; /* @noflip */ margin: 4px 0 4px 1em; width: 238px; } } 8fe3df4bb607e699eab2dbd23bd4a1a446391002 Module:Message box/ombox.css 828 373 742 2022-07-11T18:40:17Z wikipedia>Izno 0 and move mbox-small to 720px here as well text text/plain /* {{pp|small=y}} */ .ombox { margin: 4px 0; border-collapse: collapse; border: 1px solid #a2a9b1; /* Default "notice" gray */ background-color: #f8f9fa; box-sizing: border-box; } /* For the "small=yes" option. */ .ombox.mbox-small { font-size: 88%; line-height: 1.25em; } .ombox-speedy { border: 2px solid #b32424; /* Red */ background-color: #fee7e6; /* Pink */ } .ombox-delete { border: 2px solid #b32424; /* Red */ } .ombox-content { border: 1px solid #f28500; /* Orange */ } .ombox-style { border: 1px solid #fc3; /* Yellow */ } .ombox-move { border: 1px solid #9932cc; /* Purple */ } .ombox-protection { border: 2px solid #a2a9b1; /* Gray-gold */ } .ombox .mbox-text { border: none; /* @noflip */ padding: 0.25em 0.9em; width: 100%; } .ombox .mbox-image { border: none; /* @noflip */ padding: 2px 0 2px 0.9em; text-align: center; } .ombox .mbox-imageright { border: none; /* @noflip */ padding: 2px 0.9em 2px 0; text-align: center; } /* An empty narrow cell */ .ombox .mbox-empty-cell { border: none; padding: 0; width: 1px; } .ombox .mbox-invalid-type { text-align: center; } @media (min-width: 720px) { .ombox { margin: 4px 10%; } .ombox.mbox-small { /* @noflip */ clear: right; /* @noflip */ float: right; /* @noflip */ margin: 4px 0 4px 1em; width: 238px; } } 8fe3df4bb607e699eab2dbd23bd4a1a446391002 Module:Message box/ambox.css 828 478 956 2022-07-12T15:25:07Z wikipedia>Izno 0 hack around mf being opinionated text text/plain /* {{pp|small=y}} */ .ambox { border: 1px solid #a2a9b1; /* @noflip */ border-left: 10px solid #36c; /* Default "notice" blue */ background-color: #fbfbfb; box-sizing: border-box; } /* Single border between stacked boxes. Take into account base templatestyles, * user styles, and Template:Dated maintenance category. * remove link selector when T200206 is fixed */ .ambox + link + .ambox, .ambox + link + style + .ambox, .ambox + link + link + .ambox, /* TODO: raise these as "is this really that necessary???". the change was Dec 2021 */ .ambox + .mw-empty-elt + link + .ambox, .ambox + .mw-empty-elt + link + style + .ambox, .ambox + .mw-empty-elt + link + link + .ambox { margin-top: -1px; } /* For the "small=left" option. */ /* must override .ambox + .ambox styles above */ html body.mediawiki .ambox.mbox-small-left { /* @noflip */ margin: 4px 1em 4px 0; overflow: hidden; width: 238px; border-collapse: collapse; font-size: 88%; line-height: 1.25em; } .ambox-speedy { /* @noflip */ border-left: 10px solid #b32424; /* Red */ background-color: #fee7e6; /* Pink */ } .ambox-delete { /* @noflip */ border-left: 10px solid #b32424; /* Red */ } .ambox-content { /* @noflip */ border-left: 10px solid #f28500; /* Orange */ } .ambox-style { /* @noflip */ border-left: 10px solid #fc3; /* Yellow */ } .ambox-move { /* @noflip */ border-left: 10px solid #9932cc; /* Purple */ } .ambox-protection { /* @noflip */ border-left: 10px solid #a2a9b1; /* Gray-gold */ } .ambox .mbox-text { border: none; /* @noflip */ padding: 0.25em 0.5em; width: 100%; } .ambox .mbox-image { border: none; /* @noflip */ padding: 2px 0 2px 0.5em; text-align: center; } .ambox .mbox-imageright { border: none; /* @noflip */ padding: 2px 0.5em 2px 0; text-align: center; } /* An empty narrow cell */ .ambox .mbox-empty-cell { border: none; padding: 0; width: 1px; } .ambox .mbox-image-div { width: 52px; } /* Hack around MobileFrontend being opinionated */ html.client-js body.skin-minerva .mbox-text-span { margin-left: 23px !important; } @media (min-width: 720px) { .ambox { margin: 0 10%; /* 10% = Will not overlap with other elements */ } } 29898fdc5160b39a8f580c76efe77afa1f6f58a4 Template:Para 10 325 645 2022-07-22T08:06:17Z wikipedia>TheDJ 0 breakup super long words, so we do not overflow the viewport. wikitext text/x-wiki <code class="tpl-para" style="word-break:break-word;{{SAFESUBST:<noinclude />#if:{{{plain|}}}|border: none; background-color: inherit;}} {{SAFESUBST:<noinclude />#if:{{{plain|}}}{{{mxt|}}}{{{green|}}}{{{!mxt|}}}{{{red|}}}|color: {{SAFESUBST:<noinclude />#if:{{{mxt|}}}{{{green|}}}|#006400|{{SAFESUBST:<noinclude />#if:{{{!mxt|}}}{{{red|}}}|#8B0000|inherit}}}};}} {{SAFESUBST:<noinclude />#if:{{{style|}}}|{{{style}}}}}">&#124;{{SAFESUBST:<noinclude />#if:{{{1|}}}|{{{1}}}&#61;}}{{{2|}}}</code><noinclude> {{Documentation}} <!--Categories and interwikis go near the bottom of the /doc subpage.--> </noinclude> 06006deea2ed5d552aab61b4332321ab749ae7e8 Template:Plural 10 465 930 2022-08-16T09:30:03Z wikipedia>Paine Ellsworth 0 update wikitext text/x-wiki {{{1}}}{{{{{|safesubst:}}}#if:{{{{{|safesubst:}}}yesno|{{{nb|no}}}|no=}}|&nbsp;|&#32;}}{{{{{|safesubst:}}}plural:{{{1}}}|{{{2}}}|{{{3|{{{2}}}s}}}|{{{4}}}}}<noinclude> {{documentation}} <!-- Add categories to the /doc subpage; interwikis go to Wikidata, thank you! --> </noinclude> 89e87ed3de0186b2bfa1e9c61eb190eae2d72cb4 Module:Used in system 828 370 734 2022-08-20T15:58:28Z wikipedia>Pppery 0 Not an improvement - there's already a well-established edit request process starting with clicking "view source" and we don't need a duplicative process for the specific set of templates that are used in system messages wikitext text/x-wiki {{#invoke:High-use|main|1=|2={{{2|}}}|system={{#if:{{{1|}}}|{{{1}}}|in system messages}}<noinclude>|nocat=true</noinclude>}}<noinclude> {{documentation}}<!-- Add categories and interwikis to the /doc subpage, not here! --> </noinclude> 0abe278369db6cbbe319e7452d7644e27e11c532 Module:Hatnote 828 486 970 2022-09-05T18:18:32Z infoboxes>Nihiltres 0 Reordered helper functions (first by export status, then alphabetically) and migrated p.quote upstream from [[Module:Redirect hatnote]] (includes contributions by Tamzin and Nihiltres) Scribunto text/plain -------------------------------------------------------------------------------- -- Module:Hatnote -- -- -- -- This module produces hatnote links and links to related articles. It -- -- implements the {{hatnote}} and {{format link}} meta-templates and includes -- -- helper functions for other Lua hatnote modules. -- -------------------------------------------------------------------------------- local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg local mArguments -- lazily initialise [[Module:Arguments]] local yesno -- lazily initialise [[Module:Yesno]] local formatLink -- lazily initialise [[Module:Format link]] ._formatLink local p = {} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function getArgs(frame) -- Fetches the arguments from the parent frame. Whitespace is trimmed and -- blanks are removed. mArguments = require('Module:Arguments') return mArguments.getArgs(frame, {parentOnly = true}) end local function removeInitialColon(s) -- Removes the initial colon from a string, if present. return s:match('^:?(.*)') end function p.defaultClasses(inline) -- Provides the default hatnote classes as a space-separated string; useful -- for hatnote-manipulation modules like [[Module:Hatnote group]]. return (inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' .. 'navigation-not-searchable' end function p.disambiguate(page, disambiguator) -- Formats a page title with a disambiguation parenthetical, -- i.e. "Example" → "Example (disambiguation)". checkType('disambiguate', 1, page, 'string') checkType('disambiguate', 2, disambiguator, 'string', true) disambiguator = disambiguator or 'disambiguation' return mw.ustring.format('%s (%s)', page, disambiguator) end function p.findNamespaceId(link, removeColon) -- Finds the namespace id (namespace number) of a link or a pagename. This -- function will not work if the link is enclosed in double brackets. Colons -- are trimmed from the start of the link by default. To skip colon -- trimming, set the removeColon parameter to false. checkType('findNamespaceId', 1, link, 'string') checkType('findNamespaceId', 2, removeColon, 'boolean', true) if removeColon ~= false then link = removeInitialColon(link) end local namespace = link:match('^(.-):') if namespace then local nsTable = mw.site.namespaces[namespace] if nsTable then return nsTable.id end end return 0 end function p.makeWikitextError(msg, helpLink, addTrackingCategory, title) -- Formats an error message to be returned to wikitext. If -- addTrackingCategory is not false after being returned from -- [[Module:Yesno]], and if we are not on a talk page, a tracking category -- is added. checkType('makeWikitextError', 1, msg, 'string') checkType('makeWikitextError', 2, helpLink, 'string', true) yesno = require('Module:Yesno') title = title or mw.title.getCurrentTitle() -- Make the help link text. local helpText if helpLink then helpText = ' ([[' .. helpLink .. '|help]])' else helpText = '' end -- Make the category text. local category if not title.isTalkPage -- Don't categorise talk pages and title.namespace ~= 2 -- Don't categorise userspace and yesno(addTrackingCategory) ~= false -- Allow opting out then category = 'Hatnote templates with errors' category = mw.ustring.format( '[[%s:%s]]', mw.site.namespaces[14].name, category ) else category = '' end return mw.ustring.format( '<strong class="error">Error: %s%s.</strong>%s', msg, helpText, category ) end local curNs = mw.title.getCurrentTitle().namespace p.missingTargetCat = --Default missing target category, exported for use in related modules ((curNs == 0) or (curNs == 14)) and 'Articles with hatnote templates targeting a nonexistent page' or nil function p.quote(title) --Wraps titles in quotation marks. If the title starts/ends with a quotation --mark, kerns that side as with {{-'}} local quotationMarks = { ["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true } local quoteLeft, quoteRight = -- Test if start/end are quotation marks quotationMarks[string.sub(title, 1, 1)], quotationMarks[string.sub(title, -1, -1)] if quoteLeft or quoteRight then title = mw.html.create("span"):wikitext(title) end if quoteLeft then title:css("padding-left", "0.15em") end if quoteRight then title:css("padding-right", "0.15em") end return '"' .. tostring(title) .. '"' end -------------------------------------------------------------------------------- -- Hatnote -- -- Produces standard hatnote text. Implements the {{hatnote}} template. -------------------------------------------------------------------------------- function p.hatnote(frame) local args = getArgs(frame) local s = args[1] if not s then return p.makeWikitextError( 'no text specified', 'Template:Hatnote#Errors', args.category ) end return p._hatnote(s, { extraclasses = args.extraclasses, selfref = args.selfref }) end function p._hatnote(s, options) checkType('_hatnote', 1, s, 'string') checkType('_hatnote', 2, options, 'table', true) options = options or {} local inline = options.inline local hatnote = mw.html.create(inline == 1 and 'span' or 'div') local extraclasses if type(options.extraclasses) == 'string' then extraclasses = options.extraclasses end hatnote :attr('role', 'note') :addClass(p.defaultClasses(inline)) :addClass(extraclasses) :addClass(options.selfref and 'selfref' or nil) :wikitext(s) return mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' } } .. tostring(hatnote) end return p 3ae1ed7094c5005ca0896395ec9a587287a0bef1 Module:Documentation/doc 828 375 746 2022-09-11T15:13:25Z wikipedia>Pppery 0 Rv blanking wikitext text/x-wiki {{used in system}} {{Module rating|protected}} {{Lua|Module:Documentation/config|Module:Arguments|Module:Message box|Module:Module wikitext|Module:Protection banner}} {{Uses TemplateStyles|Module:Documentation/styles.css}} This module displays a blue box containing documentation for [[Help:Template|templates]], [[Wikipedia:Lua|Lua modules]], or other pages. The {{tl|documentation}} template invokes it. == Normal usage == For most uses, you should use the {{tl|documentation}} template; please see that template's page for its usage instructions and parameters. == Use in other modules == To use this module from another Lua module, first load it with <code>require</code>: <syntaxhighlight lang="lua"> local documentation = require('Module:Documentation').main </syntaxhighlight> Then you can simply call it using a table of arguments. <syntaxhighlight lang="lua"> documentation{content = 'Some documentation', ['link box'] = 'My custom link box'} </syntaxhighlight> Please refer to the [[Template:Documentation/doc|template documentation]] for usage instructions and a list of parameters. == Porting to other wikis == The module has a configuration file at [[Module:Documentation/config]] which is intended to allow easy translation and porting to other wikis. Please see the code comments in the config page for instructions. If you have any questions, or you need a feature which is not currently implemented, please leave a message at <span class="plainlinks">[https://en.wikipedia.org/wiki/Template_talk:Documentation Template talk:Documentation]</span><!-- this link uses external link syntax because it is intended to direct users from third-party wikis to the Wikipedia template talk page; in this situation, an internal link would unhelpfully just point to their local template talk page, and the existence of any given interwiki prefix cannot be assumed --> to get the attention of a developer. The messages that need to be customized to display a documentation template/module at the top of module pages are [[MediaWiki:Scribunto-doc-page-show]] and [[MediaWiki:Scribunto-doc-page-does-not-exist]]. 66a5c1cb6e80e28fd79c6c4544ecf09ced7a6360 Template:Parameter names example 10 336 667 2022-09-15T17:36:18Z wikipedia>Aidan9382 0 Hatnote should ideally be moved into documentation as to allow users to edit it regardless of the protection on the main template wikitext text/x-wiki <includeonly>{{#invoke:Parameter names example|main}}</includeonly><noinclude> {{Documentation}} </noinclude> de1e29d6ebc113e9d1649ea6a976625885db8a2f Module:Format link 828 485 968 2022-10-04T13:37:11Z infoboxes>Pppery 0 Avoid Lua erroring when we run out of expensive parser function calls Scribunto text/plain -------------------------------------------------------------------------------- -- Format link -- -- Makes a wikilink from the given link and display values. Links are escaped -- with colons if necessary, and links to sections are detected and displayed -- with " § " as a separator rather than the standard MediaWiki "#". Used in -- the {{format link}} template. -------------------------------------------------------------------------------- local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg local mArguments -- lazily initialise [[Module:Arguments]] local mError -- lazily initialise [[Module:Error]] local yesno -- lazily initialise [[Module:Yesno]] local p = {} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function getArgs(frame) -- Fetches the arguments from the parent frame. Whitespace is trimmed and -- blanks are removed. mArguments = require('Module:Arguments') return mArguments.getArgs(frame, {parentOnly = true}) end local function removeInitialColon(s) -- Removes the initial colon from a string, if present. return s:match('^:?(.*)') end local function maybeItalicize(s, shouldItalicize) -- Italicize s if s is a string and the shouldItalicize parameter is true. if s and shouldItalicize then return '<i>' .. s .. '</i>' else return s end end local function parseLink(link) -- Parse a link and return a table with the link's components. -- These components are: -- - link: the link, stripped of any initial colon (always present) -- - page: the page name (always present) -- - section: the page name (may be nil) -- - display: the display text, if manually entered after a pipe (may be nil) link = removeInitialColon(link) -- Find whether a faux display value has been added with the {{!}} magic -- word. local prePipe, display = link:match('^(.-)|(.*)$') link = prePipe or link -- Find the page, if it exists. -- For links like [[#Bar]], the page will be nil. local preHash, postHash = link:match('^(.-)#(.*)$') local page if not preHash then -- We have a link like [[Foo]]. page = link elseif preHash ~= '' then -- We have a link like [[Foo#Bar]]. page = preHash end -- Find the section, if it exists. local section if postHash and postHash ~= '' then section = postHash end return { link = link, page = page, section = section, display = display, } end local function formatDisplay(parsed, options) -- Formats a display string based on a parsed link table (matching the -- output of parseLink) and an options table (matching the input options for -- _formatLink). local page = maybeItalicize(parsed.page, options.italicizePage) local section = maybeItalicize(parsed.section, options.italicizeSection) if (not section) then return page elseif (not page) then return mw.ustring.format('§&nbsp;%s', section) else return mw.ustring.format('%s §&nbsp;%s', page, section) end end local function missingArgError(target) mError = require('Module:Error') return mError.error{message = 'Error: no link or target specified! ([[' .. target .. '#Errors|help]])' } end -------------------------------------------------------------------------------- -- Main functions -------------------------------------------------------------------------------- function p.formatLink(frame) -- The formatLink export function, for use in templates. yesno = require('Module:Yesno') local args = getArgs(frame) local link = args[1] or args.link local target = args[3] or args.target if not (link or target) then return missingArgError('Template:Format link') end return p._formatLink{ link = link, display = args[2] or args.display, target = target, italicizePage = yesno(args.italicizepage), italicizeSection = yesno(args.italicizesection), categorizeMissing = args.categorizemissing } end function p._formatLink(options) -- The formatLink export function, for use in modules. checkType('_formatLink', 1, options, 'table') local function check(key, expectedType) --for brevity checkTypeForNamedArg( '_formatLink', key, options[key], expectedType or 'string', true ) end check('link') check('display') check('target') check('italicizePage', 'boolean') check('italicizeSection', 'boolean') check('categorizeMissing') -- Normalize link and target and check that at least one is present if options.link == '' then options.link = nil end if options.target == '' then options.target = nil end if not (options.link or options.target) then return missingArgError('Module:Format link') end local parsed = parseLink(options.link) local display = options.display or parsed.display local catMissing = options.categorizeMissing local category = '' -- Find the display text if not display then display = formatDisplay(parsed, options) end -- Handle the target option if present if options.target then local parsedTarget = parseLink(options.target) parsed.link = parsedTarget.link parsed.page = parsedTarget.page end -- Test if page exists if a diagnostic category is specified if catMissing and (mw.ustring.len(catMissing) > 0) then local title = nil if parsed.page then title = mw.title.new(parsed.page) end if title and (not title.isExternal) then local success, exists = pcall(function() return title.exists end) if success and not exists then category = mw.ustring.format('[[Category:%s]]', catMissing) end end end -- Format the result as a link if parsed.link == display then return mw.ustring.format('[[:%s]]%s', parsed.link, category) else return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category) end end -------------------------------------------------------------------------------- -- Derived convenience functions -------------------------------------------------------------------------------- function p.formatPages(options, pages) -- Formats an array of pages using formatLink and the given options table, -- and returns it as an array. Nil values are not allowed. local ret = {} for i, page in ipairs(pages) do ret[i] = p._formatLink{ link = page, categorizeMissing = options.categorizeMissing, italicizePage = options.italicizePage, italicizeSection = options.italicizeSection } end return ret end return p 1253bdd2683ee4badc33856bfd5499b09a7dca1f Module:Icon 828 437 874 2022-10-21T08:04:06Z wikipedia>WOSlinker 0 use require("strict") instead of require("Module:No globals") Scribunto text/plain -- This module implements [[Template:Icon]]. require("strict") local yesNo = require("Module:Yesno") local getArgs = require("Module:Arguments").getArgs local getPlain = nil local p = {} -- Determine whether we're being called from a sandbox local sandbox = mw.getCurrentFrame():getTitle():find('sandbox', 1, true) and '/sandbox' or '' -- Implements [[Template:Icon]] -- Returns the icon image corresponding to a string (like 'B') function p._main(args, data) local data_module = 'Module:Icon/data'..sandbox data = data or mw.loadData(data_module) local code = args.class or args[1] local iconData if code then code = code:match('^%s*(.-)%s*$'):lower() -- trim whitespace and put in lower case iconData = data[code] end if not iconData then iconData = data._DEFAULT end return string.format( '[[File:%s%s%s|%s|class=noviewer|alt=%s]]', iconData.image, iconData.tooltip and '|' .. iconData.tooltip or '', iconData.link == false and '|link=' or '', args.size or '16x16px', iconData.alt or '' ) end -- Implements [[Template:Icon link]], a superset of [[Template:Icon]] -- Returns an icon, plus a suitably formatted wikilink function p._link(args, data) args.size = args.size or args.iconsize local icon = p._main(args, data) -- If no link given in args[2], default back to [[Template:Icon]] if not args[2] then return icon end -- Strip wiki markup out of link getPlain = getPlain or require("Module:Text").Text().getPlain local link = getPlain(args[2]) local display = args[3] or args[2] -- italicize display string, if requested if yesNo(args.i) or yesNo(args.italic) or yesNo(args.italics) then display = '<i>'..display..'</i>' end -- if display is link, just use standard wlink if link == display then return icon..'&nbsp;[['..link..']]' end return icon..'&nbsp;[['..link..'|'..display..']]' end function p.main(frame) local args = getArgs(frame,{parentFirst=true}) return p._main(args) end function p.link(frame) local args = getArgs(frame,{parentFirst=true}) return p._link(args) end return p 7688d9a465bd7c4caa51f7e5c02676c162d583f5 Module:Protection banner 828 357 708 2022-10-21T08:07:11Z wikipedia>WOSlinker 0 use require('strict') instead of require('Module:No globals') Scribunto text/plain -- This module implements {{pp-meta}} and its daughter templates such as -- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}. -- Initialise necessary modules. require('strict') local makeFileLink = require('Module:File link')._main local effectiveProtectionLevel = require('Module:Effective protection level')._main local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main local yesno = require('Module:Yesno') -- Lazily initialise modules and objects we don't always need. local getArgs, makeMessageBox, lang -- Set constants. local CONFIG_MODULE = 'Module:Protection banner/config' -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function makeCategoryLink(cat, sort) if cat then return string.format( '[[%s:%s|%s]]', mw.site.namespaces[14].name, cat, sort ) end end -- Validation function for the expiry and the protection date local function validateDate(dateString, dateType) if not lang then lang = mw.language.getContentLanguage() end local success, result = pcall(lang.formatDate, lang, 'U', dateString) if success then result = tonumber(result) if result then return result end end error(string.format( 'invalid %s: %s', dateType, tostring(dateString) ), 4) end local function makeFullUrl(page, query, display) return string.format( '[%s %s]', tostring(mw.uri.fullUrl(page, query)), display ) end -- Given a directed graph formatted as node -> table of direct successors, -- get a table of all nodes reachable from a given node (though always -- including the given node). local function getReachableNodes(graph, start) local toWalk, retval = {[start] = true}, {} while true do -- Can't use pairs() since we're adding and removing things as we're iterating local k = next(toWalk) -- This always gets the "first" key if k == nil then return retval end toWalk[k] = nil retval[k] = true for _,v in ipairs(graph[k]) do if not retval[v] then toWalk[v] = true end end end end -------------------------------------------------------------------------------- -- Protection class -------------------------------------------------------------------------------- local Protection = {} Protection.__index = Protection Protection.supportedActions = { edit = true, move = true, autoreview = true, upload = true } Protection.bannerConfigFields = { 'text', 'explanation', 'tooltip', 'alt', 'link', 'image' } function Protection.new(args, cfg, title) local obj = {} obj._cfg = cfg obj.title = title or mw.title.getCurrentTitle() -- Set action if not args.action then obj.action = 'edit' elseif Protection.supportedActions[args.action] then obj.action = args.action else error(string.format( 'invalid action: %s', tostring(args.action) ), 3) end -- Set level obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title) if not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then -- Users need to be autoconfirmed to move pages anyway, so treat -- semi-move-protected pages as unprotected. obj.level = '*' end -- Set expiry local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title) if effectiveExpiry == 'infinity' then obj.expiry = 'indef' elseif effectiveExpiry ~= 'unknown' then obj.expiry = validateDate(effectiveExpiry, 'expiry date') end -- Set reason if args[1] then obj.reason = mw.ustring.lower(args[1]) if obj.reason:find('|') then error('reasons cannot contain the pipe character ("|")', 3) end end -- Set protection date if args.date then obj.protectionDate = validateDate(args.date, 'protection date') end -- Set banner config do obj.bannerConfig = {} local configTables = {} if cfg.banners[obj.action] then configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason] end if cfg.defaultBanners[obj.action] then configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level] configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default end configTables[#configTables + 1] = cfg.masterBanner for i, field in ipairs(Protection.bannerConfigFields) do for j, t in ipairs(configTables) do if t[field] then obj.bannerConfig[field] = t[field] break end end end end return setmetatable(obj, Protection) end function Protection:isUserScript() -- Whether the page is a user JavaScript or CSS page. local title = self.title return title.namespace == 2 and ( title.contentModel == 'javascript' or title.contentModel == 'css' ) end function Protection:isProtected() return self.level ~= '*' end function Protection:shouldShowLock() -- Whether we should output a banner/padlock return self:isProtected() and not self:isUserScript() end -- Whether this page needs a protection category. Protection.shouldHaveProtectionCategory = Protection.shouldShowLock function Protection:isTemporary() return type(self.expiry) == 'number' end function Protection:makeProtectionCategory() if not self:shouldHaveProtectionCategory() then return '' end local cfg = self._cfg local title = self.title -- Get the expiry key fragment. local expiryFragment if self.expiry == 'indef' then expiryFragment = self.expiry elseif type(self.expiry) == 'number' then expiryFragment = 'temp' end -- Get the namespace key fragment. local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace] if not namespaceFragment and title.namespace % 2 == 1 then namespaceFragment = 'talk' end -- Define the order that key fragments are tested in. This is done with an -- array of tables containing the value to be tested, along with its -- position in the cfg.protectionCategories table. local order = { {val = expiryFragment, keypos = 1}, {val = namespaceFragment, keypos = 2}, {val = self.reason, keypos = 3}, {val = self.level, keypos = 4}, {val = self.action, keypos = 5} } --[[ -- The old protection templates used an ad-hoc protection category system, -- with some templates prioritising namespaces in their categories, and -- others prioritising the protection reason. To emulate this in this module -- we use the config table cfg.reasonsWithNamespacePriority to set the -- reasons for which namespaces have priority over protection reason. -- If we are dealing with one of those reasons, move the namespace table to -- the end of the order table, i.e. give it highest priority. If not, the -- reason should have highest priority, so move that to the end of the table -- instead. --]] table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3)) --[[ -- Define the attempt order. Inactive subtables (subtables with nil "value" -- fields) are moved to the end, where they will later be given the key -- "all". This is to cut down on the number of table lookups in -- cfg.protectionCategories, which grows exponentially with the number of -- non-nil keys. We keep track of the number of active subtables with the -- noActive parameter. --]] local noActive, attemptOrder do local active, inactive = {}, {} for i, t in ipairs(order) do if t.val then active[#active + 1] = t else inactive[#inactive + 1] = t end end noActive = #active attemptOrder = active for i, t in ipairs(inactive) do attemptOrder[#attemptOrder + 1] = t end end --[[ -- Check increasingly generic key combinations until we find a match. If a -- specific category exists for the combination of key fragments we are -- given, that match will be found first. If not, we keep trying different -- key fragment combinations until we match using the key -- "all-all-all-all-all". -- -- To generate the keys, we index the key subtables using a binary matrix -- with indexes i and j. j is only calculated up to the number of active -- subtables. For example, if there were three active subtables, the matrix -- would look like this, with 0 corresponding to the key fragment "all", and -- 1 corresponding to other key fragments. -- -- j 1 2 3 -- i -- 1 1 1 1 -- 2 0 1 1 -- 3 1 0 1 -- 4 0 0 1 -- 5 1 1 0 -- 6 0 1 0 -- 7 1 0 0 -- 8 0 0 0 -- -- Values of j higher than the number of active subtables are set -- to the string "all". -- -- A key for cfg.protectionCategories is constructed for each value of i. -- The position of the value in the key is determined by the keypos field in -- each subtable. --]] local cats = cfg.protectionCategories for i = 1, 2^noActive do local key = {} for j, t in ipairs(attemptOrder) do if j > noActive then key[t.keypos] = 'all' else local quotient = i / 2 ^ (j - 1) quotient = math.ceil(quotient) if quotient % 2 == 1 then key[t.keypos] = t.val else key[t.keypos] = 'all' end end end key = table.concat(key, '|') local attempt = cats[key] if attempt then return makeCategoryLink(attempt, title.text) end end return '' end function Protection:isIncorrect() local expiry = self.expiry return not self:shouldHaveProtectionCategory() or type(expiry) == 'number' and expiry < os.time() end function Protection:isTemplateProtectedNonTemplate() local action, namespace = self.action, self.title.namespace return self.level == 'templateeditor' and ( (action ~= 'edit' and action ~= 'move') or (namespace ~= 10 and namespace ~= 828) ) end function Protection:makeCategoryLinks() local msg = self._cfg.msg local ret = {self:makeProtectionCategory()} if self:isIncorrect() then ret[#ret + 1] = makeCategoryLink( msg['tracking-category-incorrect'], self.title.text ) end if self:isTemplateProtectedNonTemplate() then ret[#ret + 1] = makeCategoryLink( msg['tracking-category-template'], self.title.text ) end return table.concat(ret) end -------------------------------------------------------------------------------- -- Blurb class -------------------------------------------------------------------------------- local Blurb = {} Blurb.__index = Blurb Blurb.bannerTextFields = { text = true, explanation = true, tooltip = true, alt = true, link = true } function Blurb.new(protectionObj, args, cfg) return setmetatable({ _cfg = cfg, _protectionObj = protectionObj, _args = args }, Blurb) end -- Private methods -- function Blurb:_formatDate(num) -- Formats a Unix timestamp into dd Month, YYYY format. lang = lang or mw.language.getContentLanguage() local success, date = pcall( lang.formatDate, lang, self._cfg.msg['expiry-date-format'] or 'j F Y', '@' .. tostring(num) ) if success then return date end end function Blurb:_getExpandedMessage(msgKey) return self:_substituteParameters(self._cfg.msg[msgKey]) end function Blurb:_substituteParameters(msg) if not self._params then local parameterFuncs = {} parameterFuncs.CURRENTVERSION = self._makeCurrentVersionParameter parameterFuncs.EDITREQUEST = self._makeEditRequestParameter parameterFuncs.EXPIRY = self._makeExpiryParameter parameterFuncs.EXPLANATIONBLURB = self._makeExplanationBlurbParameter parameterFuncs.IMAGELINK = self._makeImageLinkParameter parameterFuncs.INTROBLURB = self._makeIntroBlurbParameter parameterFuncs.INTROFRAGMENT = self._makeIntroFragmentParameter parameterFuncs.PAGETYPE = self._makePagetypeParameter parameterFuncs.PROTECTIONBLURB = self._makeProtectionBlurbParameter parameterFuncs.PROTECTIONDATE = self._makeProtectionDateParameter parameterFuncs.PROTECTIONLEVEL = self._makeProtectionLevelParameter parameterFuncs.PROTECTIONLOG = self._makeProtectionLogParameter parameterFuncs.TALKPAGE = self._makeTalkPageParameter parameterFuncs.TOOLTIPBLURB = self._makeTooltipBlurbParameter parameterFuncs.TOOLTIPFRAGMENT = self._makeTooltipFragmentParameter parameterFuncs.VANDAL = self._makeVandalTemplateParameter self._params = setmetatable({}, { __index = function (t, k) local param if parameterFuncs[k] then param = parameterFuncs[k](self) end param = param or '' t[k] = param return param end }) end msg = msg:gsub('${(%u+)}', self._params) return msg end function Blurb:_makeCurrentVersionParameter() -- A link to the page history or the move log, depending on the kind of -- protection. local pagename = self._protectionObj.title.prefixedText if self._protectionObj.action == 'move' then -- We need the move log link. return makeFullUrl( 'Special:Log', {type = 'move', page = pagename}, self:_getExpandedMessage('current-version-move-display') ) else -- We need the history link. return makeFullUrl( pagename, {action = 'history'}, self:_getExpandedMessage('current-version-edit-display') ) end end function Blurb:_makeEditRequestParameter() local mEditRequest = require('Module:Submit an edit request') local action = self._protectionObj.action local level = self._protectionObj.level -- Get the edit request type. local requestType if action == 'edit' then if level == 'autoconfirmed' then requestType = 'semi' elseif level == 'extendedconfirmed' then requestType = 'extended' elseif level == 'templateeditor' then requestType = 'template' end end requestType = requestType or 'full' -- Get the display value. local display = self:_getExpandedMessage('edit-request-display') return mEditRequest._link{type = requestType, display = display} end function Blurb:_makeExpiryParameter() local expiry = self._protectionObj.expiry if type(expiry) == 'number' then return self:_formatDate(expiry) else return expiry end end function Blurb:_makeExplanationBlurbParameter() -- Cover special cases first. if self._protectionObj.title.namespace == 8 then -- MediaWiki namespace return self:_getExpandedMessage('explanation-blurb-nounprotect') end -- Get explanation blurb table keys local action = self._protectionObj.action local level = self._protectionObj.level local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject' -- Find the message in the explanation blurb table and substitute any -- parameters. local explanations = self._cfg.explanationBlurbs local msg if explanations[action][level] and explanations[action][level][talkKey] then msg = explanations[action][level][talkKey] elseif explanations[action][level] and explanations[action][level].default then msg = explanations[action][level].default elseif explanations[action].default and explanations[action].default[talkKey] then msg = explanations[action].default[talkKey] elseif explanations[action].default and explanations[action].default.default then msg = explanations[action].default.default else error(string.format( 'could not find explanation blurb for action "%s", level "%s" and talk key "%s"', action, level, talkKey ), 8) end return self:_substituteParameters(msg) end function Blurb:_makeImageLinkParameter() local imageLinks = self._cfg.imageLinks local action = self._protectionObj.action local level = self._protectionObj.level local msg if imageLinks[action][level] then msg = imageLinks[action][level] elseif imageLinks[action].default then msg = imageLinks[action].default else msg = imageLinks.edit.default end return self:_substituteParameters(msg) end function Blurb:_makeIntroBlurbParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('intro-blurb-expiry') else return self:_getExpandedMessage('intro-blurb-noexpiry') end end function Blurb:_makeIntroFragmentParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('intro-fragment-expiry') else return self:_getExpandedMessage('intro-fragment-noexpiry') end end function Blurb:_makePagetypeParameter() local pagetypes = self._cfg.pagetypes return pagetypes[self._protectionObj.title.namespace] or pagetypes.default or error('no default pagetype defined', 8) end function Blurb:_makeProtectionBlurbParameter() local protectionBlurbs = self._cfg.protectionBlurbs local action = self._protectionObj.action local level = self._protectionObj.level local msg if protectionBlurbs[action][level] then msg = protectionBlurbs[action][level] elseif protectionBlurbs[action].default then msg = protectionBlurbs[action].default elseif protectionBlurbs.edit.default then msg = protectionBlurbs.edit.default else error('no protection blurb defined for protectionBlurbs.edit.default', 8) end return self:_substituteParameters(msg) end function Blurb:_makeProtectionDateParameter() local protectionDate = self._protectionObj.protectionDate if type(protectionDate) == 'number' then return self:_formatDate(protectionDate) else return protectionDate end end function Blurb:_makeProtectionLevelParameter() local protectionLevels = self._cfg.protectionLevels local action = self._protectionObj.action local level = self._protectionObj.level local msg if protectionLevels[action][level] then msg = protectionLevels[action][level] elseif protectionLevels[action].default then msg = protectionLevels[action].default elseif protectionLevels.edit.default then msg = protectionLevels.edit.default else error('no protection level defined for protectionLevels.edit.default', 8) end return self:_substituteParameters(msg) end function Blurb:_makeProtectionLogParameter() local pagename = self._protectionObj.title.prefixedText if self._protectionObj.action == 'autoreview' then -- We need the pending changes log. return makeFullUrl( 'Special:Log', {type = 'stable', page = pagename}, self:_getExpandedMessage('pc-log-display') ) else -- We need the protection log. return makeFullUrl( 'Special:Log', {type = 'protect', page = pagename}, self:_getExpandedMessage('protection-log-display') ) end end function Blurb:_makeTalkPageParameter() return string.format( '[[%s:%s#%s|%s]]', mw.site.namespaces[self._protectionObj.title.namespace].talk.name, self._protectionObj.title.text, self._args.section or 'top', self:_getExpandedMessage('talk-page-link-display') ) end function Blurb:_makeTooltipBlurbParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('tooltip-blurb-expiry') else return self:_getExpandedMessage('tooltip-blurb-noexpiry') end end function Blurb:_makeTooltipFragmentParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('tooltip-fragment-expiry') else return self:_getExpandedMessage('tooltip-fragment-noexpiry') end end function Blurb:_makeVandalTemplateParameter() return mw.getCurrentFrame():expandTemplate{ title="vandal-m", args={self._args.user or self._protectionObj.title.baseText} } end -- Public methods -- function Blurb:makeBannerText(key) -- Validate input. if not key or not Blurb.bannerTextFields[key] then error(string.format( '"%s" is not a valid banner config field', tostring(key) ), 2) end -- Generate the text. local msg = self._protectionObj.bannerConfig[key] if type(msg) == 'string' then return self:_substituteParameters(msg) elseif type(msg) == 'function' then msg = msg(self._protectionObj, self._args) if type(msg) ~= 'string' then error(string.format( 'bad output from banner config function with key "%s"' .. ' (expected string, got %s)', tostring(key), type(msg) ), 4) end return self:_substituteParameters(msg) end end -------------------------------------------------------------------------------- -- BannerTemplate class -------------------------------------------------------------------------------- local BannerTemplate = {} BannerTemplate.__index = BannerTemplate function BannerTemplate.new(protectionObj, cfg) local obj = {} obj._cfg = cfg -- Set the image filename. local imageFilename = protectionObj.bannerConfig.image if imageFilename then obj._imageFilename = imageFilename else -- If an image filename isn't specified explicitly in the banner config, -- generate it from the protection status and the namespace. local action = protectionObj.action local level = protectionObj.level local namespace = protectionObj.title.namespace local reason = protectionObj.reason -- Deal with special cases first. if ( namespace == 10 or namespace == 828 or reason and obj._cfg.indefImageReasons[reason] ) and action == 'edit' and level == 'sysop' and not protectionObj:isTemporary() then -- Fully protected modules and templates get the special red "indef" -- padlock. obj._imageFilename = obj._cfg.msg['image-filename-indef'] else -- Deal with regular protection types. local images = obj._cfg.images if images[action] then if images[action][level] then obj._imageFilename = images[action][level] elseif images[action].default then obj._imageFilename = images[action].default end end end end return setmetatable(obj, BannerTemplate) end function BannerTemplate:renderImage() local filename = self._imageFilename or self._cfg.msg['image-filename-default'] or 'Transparent.gif' return makeFileLink{ file = filename, size = (self.imageWidth or 20) .. 'px', alt = self._imageAlt, link = self._imageLink, caption = self.imageCaption } end -------------------------------------------------------------------------------- -- Banner class -------------------------------------------------------------------------------- local Banner = setmetatable({}, BannerTemplate) Banner.__index = Banner function Banner.new(protectionObj, blurbObj, cfg) local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. obj.imageWidth = 40 obj.imageCaption = blurbObj:makeBannerText('alt') -- Large banners use the alt text for the tooltip. obj._reasonText = blurbObj:makeBannerText('text') obj._explanationText = blurbObj:makeBannerText('explanation') obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing. return setmetatable(obj, Banner) end function Banner:__tostring() -- Renders the banner. makeMessageBox = makeMessageBox or require('Module:Message box').main local reasonText = self._reasonText or error('no reason text set', 2) local explanationText = self._explanationText local mbargs = { page = self._page, type = 'protection', image = self:renderImage(), text = string.format( "'''%s'''%s", reasonText, explanationText and '<br />' .. explanationText or '' ) } return makeMessageBox('mbox', mbargs) end -------------------------------------------------------------------------------- -- Padlock class -------------------------------------------------------------------------------- local Padlock = setmetatable({}, BannerTemplate) Padlock.__index = Padlock function Padlock.new(protectionObj, blurbObj, cfg) local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. obj.imageWidth = 20 obj.imageCaption = blurbObj:makeBannerText('tooltip') obj._imageAlt = blurbObj:makeBannerText('alt') obj._imageLink = blurbObj:makeBannerText('link') obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action] or cfg.padlockIndicatorNames.default or 'pp-default' return setmetatable(obj, Padlock) end function Padlock:__tostring() local frame = mw.getCurrentFrame() -- The nowiki tag helps prevent whitespace at the top of articles. return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{ name = 'indicator', args = {name = self._indicatorName}, content = self:renderImage() } end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p = {} function p._exportClasses() -- This is used for testing purposes. return { Protection = Protection, Blurb = Blurb, BannerTemplate = BannerTemplate, Banner = Banner, Padlock = Padlock, } end function p._main(args, cfg, title) args = args or {} cfg = cfg or require(CONFIG_MODULE) local protectionObj = Protection.new(args, cfg, title) local ret = {} -- If a page's edit protection is equally or more restrictive than its -- protection from some other action, then don't bother displaying anything -- for the other action (except categories). if not yesno(args.catonly) and (protectionObj.action == 'edit' or args.demolevel or not getReachableNodes( cfg.hierarchy, protectionObj.level )[effectiveProtectionLevel('edit', protectionObj.title)]) then -- Initialise the blurb object local blurbObj = Blurb.new(protectionObj, args, cfg) -- Render the banner if protectionObj:shouldShowLock() then ret[#ret + 1] = tostring( (yesno(args.small) and Padlock or Banner) .new(protectionObj, blurbObj, cfg) ) end end -- Render the categories if yesno(args.category) ~= false then ret[#ret + 1] = protectionObj:makeCategoryLinks() end return table.concat(ret) end function p.main(frame, cfg) cfg = cfg or require(CONFIG_MODULE) -- Find default args, if any. local parent = frame.getParent and frame:getParent() local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')] -- Find user args, and use the parent frame if we are being called from a -- wrapper template. getArgs = getArgs or require('Module:Arguments').getArgs local userArgs = getArgs(frame, { parentOnly = defaultArgs, frameOnly = not defaultArgs }) -- Build the args table. User-specified args overwrite default args. local args = {} for k, v in pairs(defaultArgs or {}) do args[k] = v end for k, v in pairs(userArgs) do args[k] = v end return p._main(args, cfg) end return p 894f0884d4c2da1ce19d385b96f59af654b0946a Module:Video game reviews 828 469 938 2022-10-21T19:45:58Z wikipedia>WOSlinker 0 use require('strict') instead of require('Module:No globals') Scribunto text/plain require('strict') local p = {} local data = require('Module:Video game reviews/data') local yesno = require('Module:Yesno') local vgwd = require('Module:Video game wikidata') local getArgs local function getActiveSystems(args) local activeSystems = {} for k, v in pairs(args) do if data.systems[k] and yesno(v) then table.insert(activeSystems, k) end end table.sort(activeSystems, function(a, b) return data.systems[a].sortkey < data.systems[b].sortkey end) return activeSystems end local function getArgKeyTables(args) local reviewers, aggregators, awards = {}, {}, {} for k in pairs(args) do if string.match(k, data.i18n.pattern.reviewer) then table.insert(reviewers, k) elseif string.match(k, data.i18n.pattern.aggregator) then table.insert(aggregators, k) elseif string.match(k, data.i18n.pattern.award) then table.insert(awards, k) end end local function comparator(a, b) return tonumber(a:match('%d+')) < tonumber(b:match('%d+')) end table.sort(reviewers, comparator) table.sort(aggregators, comparator) table.sort(awards, comparator) return reviewers, aggregators, awards end local function getProvidedReviewersAndAggregators(args, usePlatforms) local providedReviewers, providedAggregators = {}, {} if usePlatforms then local seen = {} for k in pairs(args) do local splitPos = string.find(k, '_') if splitPos then local halfarg = string.sub(k, 1, splitPos - 1) if not seen[halfarg] then seen[halfarg] = true if data.reviewers[halfarg] then table.insert(providedReviewers, halfarg) elseif data.aggregators[halfarg] then table.insert(providedAggregators, halfarg) end end end end else for k in pairs(args) do if not string.find(k, '_') then if data.reviewers[k] then table.insert(providedReviewers, k) elseif data.aggregators[k] then table.insert(providedAggregators, k) end end end end table.sort(providedReviewers, function(a, b) return data.reviewers[a].sortkey < data.reviewers[b].sortkey end) table.sort(providedAggregators, function(a, b) return data.aggregators[a].sortkey < data.aggregators[b].sortkey end) return providedReviewers, providedAggregators end local function renderHeadingRowWithSystems(builder, activeSystems, headingText) builder:tag('tr') :addClass(data.i18n.class.headerrow) :tag('th') :attr('scope', 'col') :attr('rowspan', '2') :wikitext(headingText) :done() :tag('th') :attr('scope', 'colgroup') :attr('colspan', #activeSystems) :wikitext(data.i18n.display.score) :done() builder = builder:tag('tr') for _, v in ipairs(activeSystems) do builder:tag('th') :wikitext(data.systems[v].name) :attr('scope', 'col') :done() end end local function renderHeadingRow(builder, nameHeading) builder:tag('tr') :addClass(data.i18n.class.headerrow) :tag('th') :attr('scope', 'col') :wikitext(nameHeading) :done() :tag('th') :attr('scope', 'col') :wikitext(data.i18n.display.score) :done() end local function renderRatingsBySystem(builder, code, name, activeSystems, args, na) builder = builder:tag('tr') builder:tag('td') :wikitext(name) for _, v in ipairs(activeSystems) do local combinedCode = code .. '_' .. v local cell = builder:tag('td') if args[combinedCode] then cell :wikitext(args[combinedCode]) :done() elseif na then cell :addClass(data.i18n.class.na) :wikitext(data.i18n.display.na) :done() end end end local function renderRating(builder, name, rating) builder:tag('tr') :tag('td') :addClass(data.i18n.class.centeredpub) :wikitext(name) :done() :tag('td') :wikitext(rating) :done() end local function renderAggregators(builder, providedAggregators, activeSystems, customAggregatorKeys, args) local aggregatorCount = #providedAggregators + #customAggregatorKeys if aggregatorCount == 0 then return end builder = builder:tag('table') :addClass(data.i18n.class.aggregators) :addClass(data.i18n.class.wikitable) :addClass(args.state and 'mw-collapsible-content' or nil) :tag('caption') :wikitext(data.i18n.display[aggregatorCount == 1 and 'aggregateScore' or 'aggregateScores']) :done() if #activeSystems ~= 0 then local na = yesno(args.na) local showplatforms = #activeSystems ~= 1 or yesno(args.showplatforms) if showplatforms then renderHeadingRowWithSystems(builder, activeSystems, data.i18n.display.aggregator) else renderHeadingRow(builder, data.i18n.display.aggregator) end for _, v in ipairs(providedAggregators) do renderRatingsBySystem(builder, v, data.aggregators[v].name, activeSystems, args, na) end for _, v in ipairs(customAggregatorKeys) do renderRatingsBySystem(builder, v, args[v], activeSystems, args, na) end else renderHeadingRow(builder, data.i18n.display.aggregator) for _, v in ipairs(providedAggregators) do renderRating(builder, data.aggregators[v].name, args[v]) end for _, v in ipairs(customAggregatorKeys) do renderRating(builder, args[v], args[v .. 'Score']) end end end local function renderReviews(builder, providedReviewers, activeSystems, customReviewerKeys, args, reviewerCount, priorReviewCount) if reviewerCount == 0 then return end builder = builder:tag('table') :addClass(data.i18n.class.reviews) :addClass(data.i18n.class.wikitable) :addClass(args.state and 'mw-collapsible-content' or nil) :tag('caption') :wikitext(data.i18n.display[reviewerCount == 1 and 'reviewScore' or 'reviewScores']) :addClass(priorReviewCount > 0 and data.i18n.class.stacked or nil) :done() if #activeSystems ~= 0 then local na = yesno(args.na) local showplatforms = #activeSystems ~= 1 or yesno(args.showplatforms) if showplatforms then renderHeadingRowWithSystems(builder, activeSystems, data.i18n.display.publication) else renderHeadingRow(builder, data.i18n.display.publication) end for _, v in ipairs(providedReviewers) do renderRatingsBySystem(builder, v, data.reviewers[v].name, activeSystems, args, na) end for _, v in ipairs(customReviewerKeys) do renderRatingsBySystem(builder, v, args[v], activeSystems, args, na) end else renderHeadingRow(builder, data.i18n.display.publication) for _, v in ipairs(providedReviewers) do renderRating(builder, data.reviewers[v].name, args[v]) end for _, v in ipairs(customReviewerKeys) do renderRating(builder, args[v], args[v .. 'Score']) end end end local function renderAwards(builder, args, awardKeys, priorReviewCount) if #awardKeys == 0 then return end builder = builder:tag('table') :addClass(data.i18n.class.awards) :addClass(data.i18n.class.wikitable) :addClass(args.state and 'mw-collapsible-content' or nil) :tag('caption') :wikitext(data.i18n.display[#awardKeys == 1 and 'award' or 'awards']) :addClass(priorReviewCount > 0 and data.i18n.class.stacked or nil) :done() :tag('tr') :tag('th') :attr('scope', 'col') :wikitext(data.i18n.display.publication) :done() :tag('th') :attr('scope', 'col') :wikitext(data.i18n.display.award) :done() for _, v in ipairs(awardKeys) do builder:tag('tr') :tag('td') :wikitext(args[v .. 'Pub']) :done() :tag('td') :wikitext(args[v]) :done() end builder:done() builder:done() end local function renderEditOnWikidata(builder, wikidata, state) if not wikidata then return end builder:tag('div') :addClass(data.i18n.class.wikidata) :addClass(state and 'mw-collapsible-content' or nil) :wikitext(vgwd.getUpdateLink()) :done() end local function categorizePlatformCount(builder, platformCount) if platformCount ~= 0 then builder:wikitext(data.i18n.category.multiplatform) else builder:wikitext(data.i18n.category.singleplatform) end end local function renderTitles(builder, title, subtitle) builder:tag('div') :addClass(data.i18n.class.title) :wikitext(title or data.i18n.display.reception) :done() if subtitle then builder:tag('div') :addClass(data.i18n.class.subtitle) -- The only reason to use the subtitle is collapsible content -- So always add the related class. :addClass('mw-collapsible-content') :wikitext(subtitle) :done() end end local function render(providedReviewers, providedAggregators, awardKeys, activeSystems, customAggregatorKeys, customReviewerKeys, args, wikidata) local is_collapsible = args.title and args.state and (args.state == data.i18n.state.autocollapse or args.state == data.i18n.state.collapsed or args.state == data.i18n.state.expanded ) local div = mw.html.create('div') :attr('role', 'complementary') :addClass(data.i18n.class.container) :addClass(#activeSystems == 0 and data.i18n.class.containersingle or nil) :addClass(args.align == data.i18n.align.left and data.i18n.class.containerleft or nil) :addClass(args.align == data.i18n.align.none and data.i18n.class.containernone or nil) :addClass(is_collapsible and 'mw-collapsible' or nil) :addClass(is_collapsible and args.state == data.i18n.state.collapsed and 'mw-collapsed' or nil) :addClass(is_collapsible and args.state == data.i18n.state.autocollapse and args.state or nil) renderTitles(div, args.title, args.subtitle) local aggregatorCount = #providedAggregators + #customAggregatorKeys renderAggregators( div, providedAggregators, activeSystems, customAggregatorKeys, args, aggregatorCount ) local reviewerCount = #customReviewerKeys + #providedReviewers renderReviews( div, providedReviewers, activeSystems, customReviewerKeys, args, reviewerCount, aggregatorCount ) renderAwards( div, args, awardKeys, reviewerCount + aggregatorCount ) renderEditOnWikidata(div, wikidata, args.state) categorizePlatformCount(div, #activeSystems) return div end local function checkForWikidata(frame, args, activeSystems, providedAggregators) local wikidata = false if args.qid == 'none' then return wikidata end vgwd.setDateFormat(args.df) vgwd.setGame(args.qid) vgwd.setSystem(nil) vgwd.setGenerateReferences(true) vgwd.setShowUpdateLink(false) vgwd.setUpdateLinkStyle("text and pen") vgwd.setSystemFormat(args.systemFormat) -- Loop through aggregators if we have any. if #providedAggregators ~= 0 then for _, aggr in ipairs(providedAggregators) do -- Check if vgwd knows this aggregator. if vgwd.setReviewer(aggr) == nil then -- Loop through active systems if #activeSystems ~= 0 then for _, sys in ipairs(activeSystems) do local combinedCode = aggr .. '_' .. sys if args[combinedCode] == 'wikidata' then vgwd.setSystem(sys) vgwd.setShowSystem(false) local vgwdScore = vgwd.printReviewScores(frame) if vgwdScore then args[combinedCode] = vgwdScore end wikidata = true end end else vgwd.setShowSystem(true) if args[aggr] == 'wikidata' then local vgwdScore = vgwd.printReviewScores(frame) if vgwdScore then args[aggr] = vgwdScore end wikidata = true end end end end end return wikidata end function p._reviewbox(frame, args) local activeSystems = getActiveSystems(args) local customReviewerKeys, customAggregatorKeys, awardKeys = getArgKeyTables(args) local providedReviewers, providedAggregators = getProvidedReviewersAndAggregators(args, #activeSystems ~= 0) local wikidata = checkForWikidata(frame, args, activeSystems, providedAggregators) if #customAggregatorKeys ~= 0 or #customReviewerKeys ~= 0 or #providedAggregators ~= 0 or #providedReviewers ~= 0 or #awardKeys ~= 0 then return frame:extensionTag{ name='templatestyles', args = { src = data.i18n.templatestyles } } .. tostring(render( providedReviewers, providedAggregators, awardKeys, activeSystems, customAggregatorKeys, customReviewerKeys, args, wikidata )) elseif mw.title.getCurrentTitle().namespace == 0 then return data.i18n.category.empty end end function p.reviewbox(frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end return p._reviewbox(frame, getArgs(frame, { wrappers = data.i18n.wrapper, trim = false, translate = data.argi18n } )) end return p 29883de6139a02f8c525496cd6f849740015b6b9 Module:Video game release 828 463 926 2022-10-21T19:47:32Z wikipedia>WOSlinker 0 use require('strict') instead of require('Module:No globals') Scribunto text/plain require('strict') local getArgs = require('Module:Arguments').getArgs local cd = require('Module:CountryData') local list = require('Module:List'); local p = {} local knownargs = { ['format'] = true, ['class'] = true, ['style'] = true, ['list_style'] = true, ['item_style'] = true, ['item1_style'] = true, ['indent'] = true } local labels = { ['NA'] = "[[North America|NA]]", ['EU'] = "[[Europe|EU]]", ['EUR'] = "[[Europe|EU]]", ['AU'] = "[[Australasia|AU]]", ['AUS'] = "[[Australasia|AU]]", ['PAL'] = "[[PAL region|PAL]]", ['SEA'] = "[[Southeast Asia|SEA]]", ['AS'] = "[[Asia|AS]]", ['SA'] = "[[South America|SA]]", ['OC'] = "[[Oceania|OC]]", ['WW'] = "<abbr title=\"Worldwide\">WW</abbr>" } local function getLocalLabel(alias) local label = labels[string.upper(alias)] return label end local countryData = {}; -- Used to store country data to avoid the need of repeated calls to Module:CountryData. This saves a little time if the same abbreviation appears multiple times in the template. local function getCountryData(frame, alias) local ualias = string.upper(alias) if (countryData[ualias] == nil) then local cdtable = cd.gettable(frame, alias, {}) countryData[ualias] = cdtable['alias'] end return countryData[ualias] end local function splitLabel(s) local islist = true local res = {} for k,v in ipairs(mw.text.split(s or '', '%s*/%s*')) do local v1 = v:match('^%s*([A-Z][A-Z][A-Z]?)%s*$') if v1 then table.insert(res,v1) else local v2 = v:match('^%s*(%[%[[^%[%]|]*|[A-Z][A-Z][A-Z]?%]%])%s*$') if v2 then table.insert(res,v2) else islist = false end end end return islist and res or {s} end function p.main(frame) local args = getArgs(frame) local listformat = args['format'] if (listformat == nil or listformat == "") then listformat = "unbulleted" end local items = {} -- Old syntax "Two parameter region" use case, where param 1 is an article, param 2 is a label, and param 3 is the date. We assume this case if argument 4 is nil. if (args[3] ~= nil and args[4] == nil) then local item = "<span style=\"font-size:95%;\">[[" if (args[1] ~= nil) then item = item .. args[1] end item = item .. "|" if (args[2] ~= nil) then item = item .. args[2] end item = item .. "]]:</span> " .. args[3] .. "[[Category:Pages using vgrelease with two parameter region]]" table.insert(items, item) -- Old syntax "Blank region" use case, where param 1 is empty, and param 2 is the date. elseif (args[1] == nil and args[2] ~= nil) then local item = args[2] .. "[[Category:Pages using vgrelease without a region]]" table.insert(items, item) -- Normal use cases, region/date pairs in 1/2, 3/4, 5/6, etc. else local i = 1 local j = 2 while (args[i] and args[j]) do local labels = {} for k,v in ipairs(splitLabel(args[i])) do local label = getLocalLabel(v); -- Didn't find a local label? Check for country data. if (label == nil) then if not v:match('^%s*%[') then label = getCountryData(frame, v) end -- Found something? Build a sitelink with it. if (label ~= nil) then label = "[[" .. label .. "|" .. v .. "]]" else label = v end end table.insert(labels, label) end local item = "<span style=\"font-size:95%;\">" .. table.concat(labels,'/') .. ":</span> " .. args[j] table.insert(items, item) i = i + 2 j = j + 2 end end -- Add known parameters of Module:List to the table for k, v in pairs(args) do if (knownargs[k] == true) then items[k] = v end end local out = list.makeList(listformat, items) -- Preview message and category local parameterMsg = require('Module:If preview')._warning({ 'Unknown parameter "_VALUE_".' }) .. "[[Category:Pages using vgrelease with named parameters|_VALUE_]]" -- Check for invalid parameters for k, v in pairs(args) do if (type(k) ~= 'number' and knownargs[k] ~= true) then local msg = parameterMsg:gsub('_VALUE_', k) out = out .. msg end end return out end return p 5f7f8d3ab03e85327a71b5355a3387ed5df65e4f Module:Italic title 828 454 908 2022-10-21T21:17:12Z wikipedia>Legoktm 0 Replace [[Module:No globals]] with require( "strict" ) Scribunto text/plain -- This module implements {{italic title}}. require('strict') local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg local yesno = require('Module:Yesno') -------------------------------------------------------------------------------- -- ItalicTitle class -------------------------------------------------------------------------------- local ItalicTitle = {} do ---------------------------------------------------------------------------- -- Class attributes and functions -- Things that belong to the class are here. Things that belong to each -- object are in the constructor. ---------------------------------------------------------------------------- -- Keys of title parts that can be italicized. local italicizableKeys = { namespace = true, title = true, dab = true, } ---------------------------------------------------------------------------- -- ItalicTitle constructor -- This contains all the dynamic attributes and methods. ---------------------------------------------------------------------------- function ItalicTitle.new() local obj = {} -- Function for checking self variable in methods. local checkSelf = libraryUtil.makeCheckSelfFunction( 'ItalicTitle', 'obj', obj, 'ItalicTitle object' ) -- Checks a key is present in a lookup table. -- Param: name - the function name. -- Param: argId - integer position of the key in the argument list. -- Param: key - the key. -- Param: lookupTable - the table to look the key up in. local function checkKey(name, argId, key, lookupTable) if not lookupTable[key] then error(string.format( "bad argument #%d to '%s' ('%s' is not a valid key)", argId, name, key ), 3) end end -- Set up object structure. local parsed = false local categories = {} local italicizedKeys = {} local italicizedSubstrings = {} -- Parses a title object into its namespace text, title, and -- disambiguation text. -- Param: options - a table of options with the following keys: -- title - the title object to parse -- ignoreDab - ignore any disambiguation parentheses -- Returns the current object. function obj:parseTitle(options) checkSelf(self, 'parseTitle') checkType('parseTitle', 1, options, 'table') checkTypeForNamedArg('parseTitle', 'title', options.title, 'table') local title = options.title -- Title and dab text local prefix, parentheses if not options.ignoreDab then prefix, parentheses = mw.ustring.match( title.text, '^(.+) %(([^%(%)]+)%)$' ) end if prefix and parentheses then self.title = prefix self.dab = parentheses else self.title = title.text end -- Namespace local namespace = mw.site.namespaces[title.namespace].name if namespace and #namespace >= 1 then self.namespace = namespace end -- Register the object as having parsed a title. parsed = true return self end -- Italicizes part of the title. -- Param: key - the key of the title part to be italicized. Possible -- keys are contained in the italicizableKeys table. -- Returns the current object. function obj:italicize(key) checkSelf(self, 'italicize') checkType('italicize', 1, key, 'string') checkKey('italicize', 1, key, italicizableKeys) italicizedKeys[key] = true return self end -- Un-italicizes part of the title. -- Param: key - the key of the title part to be un-italicized. Possible -- keys are contained in the italicizableKeys table. -- Returns the current object. function obj:unitalicize(key) checkSelf(self, 'unitalicize') checkType('unitalicize', 1, key, 'string') checkKey('unitalicize', 1, key, italicizableKeys) italicizedKeys[key] = nil return self end -- Italicizes a substring in the title. This only affects the main part -- of the title, not the namespace or the disambiguation text. -- Param: s - the substring to be italicized. -- Returns the current object. function obj:italicizeSubstring(s) checkSelf(self, 'italicizeSubstring') checkType('italicizeSubstring', 1, s, 'string') italicizedSubstrings[s] = true return self end -- Un-italicizes a substring in the title. This only affects the main -- part of the title, not the namespace or the disambiguation text. -- Param: s - the substring to be un-italicized. -- Returns the current object. function obj:unitalicizeSubstring(s) checkSelf(self, 'unitalicizeSubstring') checkType('unitalicizeSubstring', 1, s, 'string') italicizedSubstrings[s] = nil return self end -- Renders the object into a page name. If no title has yet been parsed, -- the current title is used. -- Returns string function obj:renderTitle() checkSelf(self, 'renderTitle') -- Italicizes a string -- Param: s - the string to italicize -- Returns string. local function italicize(s) assert(type(s) == 'string', 's was not a string') assert(s ~= '', 's was the empty string') return string.format('<i>%s</i>', s) end -- Escape characters in a string that are magic in Lua patterns. -- Param: pattern - the pattern to escape -- Returns string. local function escapeMagicCharacters(s) assert(type(s) == 'string', 's was not a string') return s:gsub('%p', '%%%0') end -- If a title hasn't been parsed yet, parse the current title. if not parsed then self:parseTitle{title = mw.title.getCurrentTitle()} end -- Italicize the different parts of the title and store them in a -- titleParts table to be joined together later. local titleParts = {} -- Italicize the italicizable keys. for key in pairs(italicizableKeys) do if self[key] then if italicizedKeys[key] then titleParts[key] = italicize(self[key]) else titleParts[key] = self[key] end end end -- Italicize substrings. If there are any substrings to be -- italicized then start from the raw title, as this overrides any -- italicization of the main part of the title. if next(italicizedSubstrings) then titleParts.title = self.title for s in pairs(italicizedSubstrings) do local pattern = escapeMagicCharacters(s) local italicizedTitle, nReplacements = titleParts.title:gsub( pattern, italicize ) titleParts.title = italicizedTitle -- If we didn't make any replacements then it means that we -- have been passed a bad substring or that the page has -- been moved to a bad title, so add a tracking category. if nReplacements < 1 then categories['Pages using italic title with no matching string'] = true end end end -- Assemble the title together from the parts. local ret = '' if titleParts.namespace then ret = ret .. titleParts.namespace .. ':' end ret = ret .. titleParts.title if titleParts.dab then ret = ret .. ' (' .. titleParts.dab .. ')' end return ret end -- Returns an expanded DISPLAYTITLE parser function called with the -- result of obj:renderTitle, plus any other optional arguments. -- Returns string function obj:renderDisplayTitle(...) checkSelf(self, 'renderDisplayTitle') return mw.getCurrentFrame():callParserFunction( 'DISPLAYTITLE', self:renderTitle(), ... ) end -- Returns an expanded DISPLAYTITLE parser function called with the -- result of obj:renderTitle, plus any other optional arguments, plus -- any tracking categories. -- Returns string function obj:render(...) checkSelf(self, 'render') local ret = self:renderDisplayTitle(...) for cat in pairs(categories) do ret = ret .. string.format( '[[Category:%s]]', cat ) end return ret end return obj end end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p = {} local function getArgs(frame, wrapper) assert(type(wrapper) == 'string', 'wrapper was not a string') return require('Module:Arguments').getArgs(frame, { wrappers = wrapper }) end -- Main function for {{italic title}} function p._main(args) checkType('_main', 1, args, 'table') local italicTitle = ItalicTitle.new() italicTitle:parseTitle{ title = mw.title.getCurrentTitle(), ignoreDab = yesno(args.all, false) } if args.string then italicTitle:italicizeSubstring(args.string) else italicTitle:italicize('title') end return italicTitle:render(args[1]) end function p.main(frame) return p._main(getArgs(frame, 'Template:Italic title')) end function p._dabonly(args) return ItalicTitle.new() :italicize('dab') :render(args[1]) end function p.dabonly(frame) return p._dabonly(getArgs(frame, 'Template:Italic dab')) end return p 9b49016c8f526f680e9ecdaf36ec4ceafda2a2f8 Module:Message box 828 349 692 2022-10-23T04:26:44Z wikipedia>Legoktm 0 Replace [[Module:No globals]] with require( "strict" ) Scribunto text/plain require('strict') local getArgs local yesno = require('Module:Yesno') local lang = mw.language.getContentLanguage() local CONFIG_MODULE = 'Module:Message box/configuration' local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function getTitleObject(...) -- Get the title object, passing the function through pcall -- in case we are over the expensive function count limit. local success, title = pcall(mw.title.new, ...) if success then return title end end local function union(t1, t2) -- Returns the union of two arrays. local vals = {} for i, v in ipairs(t1) do vals[v] = true end for i, v in ipairs(t2) do vals[v] = true end local ret = {} for k in pairs(vals) do table.insert(ret, k) end table.sort(ret) return ret end local function getArgNums(args, prefix) local nums = {} for k, v in pairs(args) do local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$') if num then table.insert(nums, tonumber(num)) end end table.sort(nums) return nums end -------------------------------------------------------------------------------- -- Box class definition -------------------------------------------------------------------------------- local MessageBox = {} MessageBox.__index = MessageBox function MessageBox.new(boxType, args, cfg) args = args or {} local obj = {} -- Set the title object and the namespace. obj.title = getTitleObject(args.page) or mw.title.getCurrentTitle() -- Set the config for our box type. obj.cfg = cfg[boxType] if not obj.cfg then local ns = obj.title.namespace -- boxType is "mbox" or invalid input if args.demospace and args.demospace ~= '' then -- implement demospace parameter of mbox local demospace = string.lower(args.demospace) if DEMOSPACES[demospace] then -- use template from DEMOSPACES obj.cfg = cfg[DEMOSPACES[demospace]] elseif string.find( demospace, 'talk' ) then -- demo as a talk page obj.cfg = cfg.tmbox else -- default to ombox obj.cfg = cfg.ombox end elseif ns == 0 then obj.cfg = cfg.ambox -- main namespace elseif ns == 6 then obj.cfg = cfg.imbox -- file namespace elseif ns == 14 then obj.cfg = cfg.cmbox -- category namespace else local nsTable = mw.site.namespaces[ns] if nsTable and nsTable.isTalk then obj.cfg = cfg.tmbox -- any talk namespace else obj.cfg = cfg.ombox -- other namespaces or invalid input end end end -- Set the arguments, and remove all blank arguments except for the ones -- listed in cfg.allowBlankParams. do local newArgs = {} for k, v in pairs(args) do if v ~= '' then newArgs[k] = v end end for i, param in ipairs(obj.cfg.allowBlankParams or {}) do newArgs[param] = args[param] end obj.args = newArgs end -- Define internal data structure. obj.categories = {} obj.classes = {} -- For lazy loading of [[Module:Category handler]]. obj.hasCategories = false return setmetatable(obj, MessageBox) end function MessageBox:addCat(ns, cat, sort) if not cat then return nil end if sort then cat = string.format('[[Category:%s|%s]]', cat, sort) else cat = string.format('[[Category:%s]]', cat) end self.hasCategories = true self.categories[ns] = self.categories[ns] or {} table.insert(self.categories[ns], cat) end function MessageBox:addClass(class) if not class then return nil end table.insert(self.classes, class) end function MessageBox:setParameters() local args = self.args local cfg = self.cfg -- Get type data. self.type = args.type local typeData = cfg.types[self.type] self.invalidTypeError = cfg.showInvalidTypeError and self.type and not typeData typeData = typeData or cfg.types[cfg.default] self.typeClass = typeData.class self.typeImage = typeData.image -- Find if the box has been wrongly substituted. self.isSubstituted = cfg.substCheck and args.subst == 'SUBST' -- Find whether we are using a small message box. self.isSmall = cfg.allowSmall and ( cfg.smallParam and args.small == cfg.smallParam or not cfg.smallParam and yesno(args.small) ) -- Add attributes, classes and styles. self.id = args.id self.name = args.name if self.name then self:addClass('box-' .. string.gsub(self.name,' ','_')) end if yesno(args.plainlinks) ~= false then self:addClass('plainlinks') end for _, class in ipairs(cfg.classes or {}) do self:addClass(class) end if self.isSmall then self:addClass(cfg.smallClass or 'mbox-small') end self:addClass(self.typeClass) self:addClass(args.class) self.style = args.style self.attrs = args.attrs -- Set text style. self.textstyle = args.textstyle -- Find if we are on the template page or not. This functionality is only -- used if useCollapsibleTextFields is set, or if both cfg.templateCategory -- and cfg.templateCategoryRequireName are set. self.useCollapsibleTextFields = cfg.useCollapsibleTextFields if self.useCollapsibleTextFields or cfg.templateCategory and cfg.templateCategoryRequireName then if self.name then local templateName = mw.ustring.match( self.name, '^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$' ) or self.name templateName = 'Template:' .. templateName self.templateTitle = getTitleObject(templateName) end self.isTemplatePage = self.templateTitle and mw.title.equals(self.title, self.templateTitle) end -- Process data for collapsible text fields. At the moment these are only -- used in {{ambox}}. if self.useCollapsibleTextFields then -- Get the self.issue value. if self.isSmall and args.smalltext then self.issue = args.smalltext else local sect if args.sect == '' then sect = 'This ' .. (cfg.sectionDefault or 'page') elseif type(args.sect) == 'string' then sect = 'This ' .. args.sect end local issue = args.issue issue = type(issue) == 'string' and issue ~= '' and issue or nil local text = args.text text = type(text) == 'string' and text or nil local issues = {} table.insert(issues, sect) table.insert(issues, issue) table.insert(issues, text) self.issue = table.concat(issues, ' ') end -- Get the self.talk value. local talk = args.talk -- Show talk links on the template page or template subpages if the talk -- parameter is blank. if talk == '' and self.templateTitle and ( mw.title.equals(self.templateTitle, self.title) or self.title:isSubpageOf(self.templateTitle) ) then talk = '#' elseif talk == '' then talk = nil end if talk then -- If the talk value is a talk page, make a link to that page. Else -- assume that it's a section heading, and make a link to the talk -- page of the current page with that section heading. local talkTitle = getTitleObject(talk) local talkArgIsTalkPage = true if not talkTitle or not talkTitle.isTalkPage then talkArgIsTalkPage = false talkTitle = getTitleObject( self.title.text, mw.site.namespaces[self.title.namespace].talk.id ) end if talkTitle and talkTitle.exists then local talkText if self.isSmall then local talkLink = talkArgIsTalkPage and talk or (talkTitle.prefixedText .. '#' .. talk) talkText = string.format('([[%s|talk]])', talkLink) else talkText = 'Relevant discussion may be found on' if talkArgIsTalkPage then talkText = string.format( '%s [[%s|%s]].', talkText, talk, talkTitle.prefixedText ) else talkText = string.format( '%s the [[%s#%s|talk page]].', talkText, talkTitle.prefixedText, talk ) end end self.talk = talkText end end -- Get other values. self.fix = args.fix ~= '' and args.fix or nil local date if args.date and args.date ~= '' then date = args.date elseif args.date == '' and self.isTemplatePage then date = lang:formatDate('F Y') end if date then self.date = string.format(" <span class='date-container'><i>(<span class='date'>%s</span>)</i></span>", date) end self.info = args.info if yesno(args.removalnotice) then self.removalNotice = cfg.removalNotice end end -- Set the non-collapsible text field. At the moment this is used by all box -- types other than ambox, and also by ambox when small=yes. if self.isSmall then self.text = args.smalltext or args.text else self.text = args.text end -- Set the below row. self.below = cfg.below and args.below -- General image settings. self.imageCellDiv = not self.isSmall and cfg.imageCellDiv self.imageEmptyCell = cfg.imageEmptyCell -- Left image settings. local imageLeft = self.isSmall and args.smallimage or args.image if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none' or not cfg.imageCheckBlank and imageLeft ~= 'none' then self.imageLeft = imageLeft if not imageLeft then local imageSize = self.isSmall and (cfg.imageSmallSize or '30x30px') or '40x40px' self.imageLeft = string.format('[[File:%s|%s|link=|alt=]]', self.typeImage or 'Imbox notice.png', imageSize) end end -- Right image settings. local imageRight = self.isSmall and args.smallimageright or args.imageright if not (cfg.imageRightNone and imageRight == 'none') then self.imageRight = imageRight end -- set templatestyles self.base_templatestyles = cfg.templatestyles self.templatestyles = args.templatestyles end function MessageBox:setMainspaceCategories() local args = self.args local cfg = self.cfg if not cfg.allowMainspaceCategories then return nil end local nums = {} for _, prefix in ipairs{'cat', 'category', 'all'} do args[prefix .. '1'] = args[prefix] nums = union(nums, getArgNums(args, prefix)) end -- The following is roughly equivalent to the old {{Ambox/category}}. local date = args.date date = type(date) == 'string' and date local preposition = 'from' for _, num in ipairs(nums) do local mainCat = args['cat' .. tostring(num)] or args['category' .. tostring(num)] local allCat = args['all' .. tostring(num)] mainCat = type(mainCat) == 'string' and mainCat allCat = type(allCat) == 'string' and allCat if mainCat and date and date ~= '' then local catTitle = string.format('%s %s %s', mainCat, preposition, date) self:addCat(0, catTitle) catTitle = getTitleObject('Category:' .. catTitle) if not catTitle or not catTitle.exists then self:addCat(0, 'Articles with invalid date parameter in template') end elseif mainCat and (not date or date == '') then self:addCat(0, mainCat) end if allCat then self:addCat(0, allCat) end end end function MessageBox:setTemplateCategories() local args = self.args local cfg = self.cfg -- Add template categories. if cfg.templateCategory then if cfg.templateCategoryRequireName then if self.isTemplatePage then self:addCat(10, cfg.templateCategory) end elseif not self.title.isSubpage then self:addCat(10, cfg.templateCategory) end end -- Add template error categories. if cfg.templateErrorCategory then local templateErrorCategory = cfg.templateErrorCategory local templateCat, templateSort if not self.name and not self.title.isSubpage then templateCat = templateErrorCategory elseif self.isTemplatePage then local paramsToCheck = cfg.templateErrorParamsToCheck or {} local count = 0 for i, param in ipairs(paramsToCheck) do if not args[param] then count = count + 1 end end if count > 0 then templateCat = templateErrorCategory templateSort = tostring(count) end if self.categoryNums and #self.categoryNums > 0 then templateCat = templateErrorCategory templateSort = 'C' end end self:addCat(10, templateCat, templateSort) end end function MessageBox:setAllNamespaceCategories() -- Set categories for all namespaces. if self.invalidTypeError then local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort) end if self.isSubstituted then self:addCat('all', 'Pages with incorrectly substituted templates') end end function MessageBox:setCategories() if self.title.namespace == 0 then self:setMainspaceCategories() elseif self.title.namespace == 10 then self:setTemplateCategories() end self:setAllNamespaceCategories() end function MessageBox:renderCategories() if not self.hasCategories then -- No categories added, no need to pass them to Category handler so, -- if it was invoked, it would return the empty string. -- So we shortcut and return the empty string. return "" end -- Convert category tables to strings and pass them through -- [[Module:Category handler]]. return require('Module:Category handler')._main{ main = table.concat(self.categories[0] or {}), template = table.concat(self.categories[10] or {}), all = table.concat(self.categories.all or {}), nocat = self.args.nocat, page = self.args.page } end function MessageBox:export() local root = mw.html.create() -- Add the subst check error. if self.isSubstituted and self.name then root:tag('b') :addClass('error') :wikitext(string.format( 'Template <code>%s[[Template:%s|%s]]%s</code> has been incorrectly substituted.', mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}') )) end local frame = mw.getCurrentFrame() root:wikitext(frame:extensionTag{ name = 'templatestyles', args = { src = self.base_templatestyles }, }) -- Add support for a single custom templatestyles sheet. Undocumented as -- need should be limited and many templates using mbox are substed; we -- don't want to spread templatestyles sheets around to arbitrary places if self.templatestyles then root:wikitext(frame:extensionTag{ name = 'templatestyles', args = { src = self.templatestyles }, }) end -- Create the box table. local boxTable = root:tag('table') boxTable:attr('id', self.id or nil) for i, class in ipairs(self.classes or {}) do boxTable:addClass(class or nil) end boxTable :cssText(self.style or nil) :attr('role', 'presentation') if self.attrs then boxTable:attr(self.attrs) end -- Add the left-hand image. local row = boxTable:tag('tr') if self.imageLeft then local imageLeftCell = row:tag('td'):addClass('mbox-image') if self.imageCellDiv then -- If we are using a div, redefine imageLeftCell so that the image -- is inside it. Divs use style="width: 52px;", which limits the -- image width to 52px. If any images in a div are wider than that, -- they may overlap with the text or cause other display problems. imageLeftCell = imageLeftCell:tag('div'):addClass('mbox-image-div') end imageLeftCell:wikitext(self.imageLeft or nil) elseif self.imageEmptyCell then -- Some message boxes define an empty cell if no image is specified, and -- some don't. The old template code in templates where empty cells are -- specified gives the following hint: "No image. Cell with some width -- or padding necessary for text cell to have 100% width." row:tag('td') :addClass('mbox-empty-cell') end -- Add the text. local textCell = row:tag('td'):addClass('mbox-text') if self.useCollapsibleTextFields then -- The message box uses advanced text parameters that allow things to be -- collapsible. At the moment, only ambox uses this. textCell:cssText(self.textstyle or nil) local textCellDiv = textCell:tag('div') textCellDiv :addClass('mbox-text-span') :wikitext(self.issue or nil) if (self.talk or self.fix) then textCellDiv:tag('span') :addClass('hide-when-compact') :wikitext(self.talk and (' ' .. self.talk) or nil) :wikitext(self.fix and (' ' .. self.fix) or nil) end textCellDiv:wikitext(self.date and (' ' .. self.date) or nil) if self.info and not self.isSmall then textCellDiv :tag('span') :addClass('hide-when-compact') :wikitext(self.info and (' ' .. self.info) or nil) end if self.removalNotice then textCellDiv:tag('span') :addClass('hide-when-compact') :tag('i') :wikitext(string.format(" (%s)", self.removalNotice)) end else -- Default text formatting - anything goes. textCell :cssText(self.textstyle or nil) :wikitext(self.text or nil) end -- Add the right-hand image. if self.imageRight then local imageRightCell = row:tag('td'):addClass('mbox-imageright') if self.imageCellDiv then -- If we are using a div, redefine imageRightCell so that the image -- is inside it. imageRightCell = imageRightCell:tag('div'):addClass('mbox-image-div') end imageRightCell :wikitext(self.imageRight or nil) end -- Add the below row. if self.below then boxTable:tag('tr') :tag('td') :attr('colspan', self.imageRight and '3' or '2') :addClass('mbox-text') :cssText(self.textstyle or nil) :wikitext(self.below or nil) end -- Add error message for invalid type parameters. if self.invalidTypeError then root:tag('div') :addClass('mbox-invalid-type') :wikitext(string.format( 'This message box is using an invalid "type=%s" parameter and needs fixing.', self.type or '' )) end -- Add categories. root:wikitext(self:renderCategories() or nil) return tostring(root) end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p, mt = {}, {} function p._exportClasses() -- For testing. return { MessageBox = MessageBox } end function p.main(boxType, args, cfgTables) local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE)) box:setParameters() box:setCategories() return box:export() end function mt.__index(t, k) return function (frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end return t.main(k, getArgs(frame, {trim = false, removeBlanks = false})) end end return setmetatable(p, mt) bdb0ecc9f26f26b9c0ce12a066a183ac9d4f0705 Template:Trim 10 446 892 2022-10-26T14:01:15Z wikipedia>MSGJ 0 x parameter not needed wikitext text/x-wiki <includeonly>{{safesubst:#if:1|{{{1|}}}}}</includeonly><noinclude> {{Documentation}} </noinclude> 3d29fbfff9683523147db6e1f55c0e17ed30863b Module:Module link 828 519 1046 2022-11-04T13:34:39Z string2>Plastikspork 0 [[Wikipedia:Templates for discussion/Log/2022 October 24#Template:M2]] closed as delete ([[WP:XFDC#4.0.13|XFDcloser]]) wikitext text/x-wiki &#123;&#123;{{{{{|safesubst:}}}#invoke:Separated entries|main|[[Module:{{{1}}}{{{section|}}}|#invoke:{{{1}}}]]|{{{2|''function''}}}|separator=&#124;}}&#125;&#125;<noinclude> {{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> 4dbda5bd7aa68e983beca7d836c547f8d146f595 Module:Icon/data 828 438 876 2022-12-02T06:35:43Z wikipedia>Paine Ellsworth 0 per edit request on talk page - include icons for Mediawiki, Phabricator and Wikitech Scribunto text/plain -- This module stores icon data for [[Module:Icon]]. -------------------------------------------------------------------------------- -- Icon data -------------------------------------------------------------------------------- local data = { fa = { image = "Featured article star.svg", tooltip = "Featured article", link = true, }, far = { image = "Cscr-star piece.png", tooltip = "Featured article review", link = true, }, farc = { image = "Cscr-star piece.png", tooltip = "Featured article removal candidate", link = true, }, ffa = { aliases = {"dfa"}, image = "Featured article star - cross.svg", tooltip = "Former featured article", link = true, }, fac = { aliases = {"fan"}, image = "Cscr-candidate.svg", tooltip = "Featured article candidate", link = true, }, ffac = { aliases = {"nofa"}, image = "Featured article star - cross.svg", tooltip = "Failed featured article candidate", link = true, }, fl = { image = "Featured article star.svg", tooltip = "Featured list", link = true, }, flrc = { aliases = {"flr"}, image = "Cscr-star piece.png", tooltip = "Featured list removal candidate", link = true, }, ffl = { aliases = {"dfl"}, image = "Cscr-featured-strike.svg", tooltip = "Former featured list", link = true, }, flc = { aliases = {"fln"}, image = "Cscr-candidate.svg", tooltip = "Featured list candidate", link = true, }, fflc = { aliases = {"nofl"}, image = "Cscr-former.svg", tooltip = "Failed featured list candidate", link = true, }, a = { image = "Symbol a class.svg", tooltip = "A-Class article", link = true, }, dac = { aliases = {"daa"}, image = "Symbol unsupport A vote.svg", tooltip = "Demoted A-Class article", link = true, }, acc = { aliases = {"acn", "aac"}, image = "A candidate.svg", tooltip = "A-Class article candidate", link = true, }, noac = { aliases = {"faac"}, image = "Symbol unsupport A vote.svg", tooltip = "Failed A-Class article candidate", link = true, }, ga = { image = "Symbol support vote.svg", tooltip = "Good article", link = false, }, gar = { image = "GA Candidate Neutral vote(ChaosNil).svg", tooltip = "Good article reassessment", link = false, }, dga = { image = "Symbol unsupport vote.svg", tooltip = "Delisted good article", link = false, }, gan = { aliases = {"gac"}, image = "GA candidate.svg", tooltip = "Good article nominee", link = false, }, ga2 = { image = "Symbol neutral vote.svg", tooltip = "Good article, 2nd opinion", link = false, }, gah = { image = "Symbol wait.svg", tooltip = "Good article on hold", link = false, }, fgan = { aliases = {"noga", "gaf", "gf"}, image = "Symbol oppose vote.svg", tooltip = "Failed good article nominee", link = false, }, fp = { image = "Cscr-featured.svg", tooltip = "Featured picture", link = true, }, fpc = { aliases = {"fpn"}, image = "Cscr-candidate.svg", tooltip = "Featured picture candidate", link = true, }, ffp = { image = "Cscr-former.svg", tooltip = "Former featured picture", link = true, }, vp = { image = "ENWP VP Logo.svg", tooltip = "Valued picture", link = true, }, vpc = { image = "Valued pics 1.svg", tooltip = "Valued picture candidate", link = true, }, fs = { image = "Cscr-featured.svg", tooltip = "Featured sound", link = true, }, ffs = { image = "Cscr-former.svg", tooltip = "Former featured sound", link = true, }, fsc = { image = "Cscr-candidate.svg", tooltip = "Featured sound candidate", link = true, }, fpo = { image = "Linecons big-star.svg", tooltip = "Before the featured portal process ceased in 2017, this had been designated as a featured portal.", link = true, }, fpor = { image = "Cscr-star piece.png", tooltip = "Featured portal review", link = true, }, ffpo = { image = "Featured article star - cross.svg", tooltip = "Former featured portal", link = true, }, fpoc = { image = "Cscr-candidate.svg", tooltip = "Featured portal candidate", link = true, }, ft = { image = "Cscr-featuredtopic.svg", tooltip = "Featured topic", link = true, }, ftrc = { image = "Cscr-star piece.png", tooltip = "Featured topic removal candidate", link = true, }, fft = { aliases = {"dft"}, image = "DFT candidate_cluster.svg", tooltip = "Former featured topic", link = true, }, ftc = { aliases = {"ftn"}, image = "FT candidate cluster.svg", tooltip = "Featured topic candidate", link = false, }, gt = { image = "Support cluster.svg", tooltip = "Good topic", link = false, }, gtrc = { image = "Symbol unsupport vote.svg", tooltip = "Good topic removal candidate", link = false, }, gtc = { aliases = {"gtn"}, image = "GA candidate cluster.svg", tooltip = "Good topic candidate", link = false, }, bplus = { aliases = {"b+"}, image = "Symbol bplus class.svg", tooltip = "Bplus-Class article", link = true, }, b = { image = "Symbol b class.svg", tooltip = "B-Class article", link = true, }, br = { aliases = {"bcr"}, image = "Bclass-checklist.svg", tooltip = "B-Class review", link = true, }, c = { image = "Symbol c class.svg", tooltip = "C-Class article", link = true, }, start = { image = "Symbol start class.svg", tooltip = "Start-Class article", link = true, }, stub = { image = "Symbol stub class.svg", tooltip = "Stub-Class article", link = true, }, list = { aliases = {"comparison"}, image = "Symbol list class.svg", tooltip = "List-Class article", link = false, }, no = { image = "Crystal button cancel.svg", tooltip = "Unknown-Class article", link = true, }, book = { image = "Symbol book class2.svg", tooltip = "Wikipedia book", link = true, }, category = { aliases = {"cat", "categ"}, image = "Symbol category class.svg", tooltip = "Category", link = false, }, disambiguation = { aliases = {"dab", "disamb", "disambig"}, image = "Symbol dab class.svg", tooltip = "Disambiguation page", link = true, }, image = { aliases = {"file"}, image = "Symbol file class.svg", tooltip = "File", link = true, }, needed = { image = "Symbol needed class.svg", tooltip = "Needed article", link = false, }, outline = { image = "Global thinking.svg", tooltip = "Outline", link = false, }, portal = { image = "Symbol portal class.svg", tooltip = "Portal", link = true, }, project = { image = "Symbol project class.svg", tooltip = "Project page", link = false, }, redirect = { aliases = {"red", "redir"}, image = "Symbol redirect vote2.svg", tooltip = "Redirect", link = true, }, template = { aliases = {"temp", "templ"}, image = "Symbol template class.svg", tooltip = "Template", link = false, }, essay = { image = "Essay.svg", tooltip = "Essay", link = false, }, na = { image = "Symbol na class.svg", tooltip = "Non-article page", link = true, }, aa = { image = "Yes check.svg", tooltip = "Audited article of limited subject matter", link = false, }, da = { image = "Symbol oppose vote.svg", tooltip = "Demoted article", link = false, }, dyk = { image = "Symbol question.svg", tooltip = "Did You Know?", link = false, }, dyk2 = { image = "DYK questionmark icon.svg", tooltip = "Did You Know?", link = false, }, pr = { image = "Nuvola apps kedit.png", tooltip = "Peer review", link = true, }, ppr = { image = "Nuvola apps kedit.png", tooltip = "Portal peer review", link = true, }, q = { aliases = {"question"}, image = "Symbol question.svg", tooltip = "Question", link = false, }, cleanup = { image = "Edit-clear.svg", tooltip = "Cleanup work", link = false, }, qi = { image = "Quality images logo.svg", tooltip = "Quality image on Wikimedia Commons", link = false, }, vi = { image = "Valued image seal.svg", tooltip = "Valued image on Wikimedia Commons", link = false, }, tfa = { image = "Wikipedia-logo.svg", tooltip = "Today's Featured Article", link = true, }, tfl = { image = "Wikipedia-logo.svg", tooltip = "Today's Featured List", link = true, }, itn = { image = "Globe current.svg", tooltip = "In The News", link = true, }, otd = { image = "Nuvola apps date.svg", tooltip = "On This Day", link = true, }, wikiproject = { image = "People icon.svg", tooltip = "WikiProject", link = false, }, goce = { image = "Writing Magnifying.PNG", tooltip = "Guild of Copy Editors", link = true, }, wikipedia = { image = "Wikipedia-logo.svg", tooltip = "Wikipedia page", link = true, }, commons = { image = "Commons-logo.svg", tooltip = "Commons page", link = false, }, wikiquote = { image = "Wikiquote-logo.svg", tooltip = "Wikiquote page", link = false, }, wikiversity = { image = "Wikiversity logo 2017.svg", tooltip = "Wikiversity page", link = true, }, wikibooks = { image = "Wikibooks-logo.svg", tooltip = "Wikibooks page", link = true, }, wikisource = { image = "Wikisource-logo.svg", tooltip = "Wikisource page", link = true, }, wiktionary = { image = "Wiktionary-logo.svg", tooltip = "Wiktionary page", link = true, }, wikinews = { image = "Wikinews-logo.svg", tooltip = "Wikinews page", link = true, }, wikispecies = { image = "Wikispecies-logo.svg", tooltip = "Wikispecies page", link = true, }, wikidata = { image = "Wikidata-logo.svg", tooltip = "Wikidata page", link = false, }, wikivoyage = { image = "Wikivoyage-logo.svg", tooltip = "Wikivoyage page", link = true, }, mediawiki = { image = "MediaWiki-2020-icon.svg", tooltip = "MediaWiki", link = false, }, phabricator = { aliases = {"phab"}, image = "Favicon-Phabricator-WM.svg", tooltip = "Phabricator", link = false, }, wikitech = { image = "Wikitech-2021-blue-icon.svg", tooltip = "Wikitech", link = false, }, meta = { image = "Wikimedia Community Logo.svg", tooltip = "Meta-wiki page", link = false, }, four = { aliases = {"4a"}, image = "Four Award.svg", tooltip = "Four Award", link = false, }, million = { image = "Million award logo.svg", tooltip = "Million Award", link = true, }, module = { image = "Lua-logo-nolabel.svg", tooltip = "Module", link = false, }, vital = { image = "Círculos_Concéntricos.svg", tooltip = "Vital article", link = false, }, potd = { image = "Wikipedia-logo.svg", tooltip = "Picture of the Day", link = true, }, _DEFAULT = { image = "Symbol question.svg", link = false, } } -------------------------------------------------------------------------------- -- End icon data -------------------------------------------------------------------------------- -- Make aliases work the same as normal keys, and remove the "aliases" subtables. local ret= {} for code, iconData in pairs(data) do iconData.canonicalCode = code if iconData.aliases then for _, alias in ipairs(iconData.aliases) do ret[alias] = iconData end iconData.aliases = nil end ret[code] = iconData end return ret d8e668d4755103abdbc8325fa35964e99198c29d Template:Plainlist/styles.css 10 382 762 2022-12-11T06:59:53Z wikipedia>Izno 0 add this reset from mobile.css text text/plain /* {{pp-template|small=yes}} */ .plainlist ol, .plainlist ul { line-height: inherit; list-style: none; margin: 0; padding: 0; /* Reset Minerva default */ } .plainlist ol li, .plainlist ul li { margin-bottom: 0; } 51706efa229ff8794c0d94f260a208e7c5e6ec30 Module:Plainlist/styles.css 828 388 776 2022-12-11T06:59:53Z wikipedia>Izno 0 add this reset from mobile.css text text/plain /* {{pp-template|small=yes}} */ .plainlist ol, .plainlist ul { line-height: inherit; list-style: none; margin: 0; padding: 0; /* Reset Minerva default */ } .plainlist ol li, .plainlist ul li { margin-bottom: 0; } 51706efa229ff8794c0d94f260a208e7c5e6ec30 Template:Infobox video game 10 458 916 2022-12-14T11:38:14Z wikipedia>Hellknowz 0 adjust auto-generated short description pattern match to 1DDD or 2DDD from just DDDD, which matches output of embedded templates incorrectly to codes like UNIQ--ref-00000003-QINU wikitext text/x-wiki {{main other|{{short description|2=noreplace|{{if both|{{{released|{{{release|}}}}}}|{{#invoke:String|match|{{{released|{{{release|}}}}}}|[12]%d%d%d|match=1|nomatch=}}|{{#invoke:String|match|{{{released|{{{release|}}}}}}|[12]%d%d%d|match=1|nomatch=}} video game|Video game}}}}}}{{#invoke:infobox|infoboxTemplate <!-- Start and styling --> | child = {{{child|}}} | subbox = {{{subbox|}}} | bodyclass = ib-video-game hproduct {{#ifeq:{{{collapsible|}}}|yes|collapsible {{#if:{{{state|}}}|{{{state}}}|autocollapse}}}} | templatestyles = Infobox video game/styles.css | aboveclass = fn | italic title = {{{italic title|<noinclude>no</noinclude>}}} <!-- Title --> | above = <includeonly>{{{title|{{#if:{{#invoke:WikidataIB|label}}|{{#invoke:WikidataIB|label}}|{{PAGENAMEBASE}}}}}}}</includeonly> <!-- Image --> | image = {{#invoke:InfoboxImage|InfoboxImage|image={{#invoke:WikidataIB |getValue|rank=best|P18 |name=image |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=no |noicon=yes|{{{image|}}}}}|size={{{image size|{{{image_size|{{{imagesize|}}}}}}}}}|sizedefault=frameless|upright={{{image_upright|1}}}|alt={{{alt|}}}|border={{{border|}}}|suppressplaceholder=yes}} | caption = {{#if:{{{image|}}}|{{{caption|}}}|{{{caption|{{#invoke:WikidataIB|getValue|P18|qual=P2096|qualsonly=y|fwd=ALL}}}}}}} <!-- Start of content --> | label2 = [[Video game developer|Developer(s)]] | data2 = {{{developer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P178|qid={{{qid|}}}|name=developer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{developer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P178}}}}}}}}} | label3 = [[Video game publisher|Publisher(s)]] | data3 = {{{publisher|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P123|qid={{{qid|}}}|name=publisher|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{publisher|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P123}}}}}}}}} | label4 = [[Video game creative director|Director(s)]] | data4 = {{{director|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P57|qid={{{qid|}}}|name=director|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{director|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P57}}}}}}}}} | label5 = [[Video game producer|Producer(s)]] | data5 = {{{producer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P162|qid={{{qid|}}}|name=producer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{producer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P162}}}}}}}}} | label6 = [[Video game designer|Designer(s)]] | data6 = {{{designer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P287|qid={{{qid|}}}|name=designer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{designer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P287}}}}}}}}} | label7 = [[Video game programmer|Programmer(s)]] | data7 = {{{programmer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P943|qid={{{qid|}}}|name=programmer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{programmer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P943}}}}}}}}} | label8 = [[Video game artist|Artist(s)]] | data8 = {{{artist|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P3080|qid={{{qid|}}}|name=artist|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{artist|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P3080}}}}}}}}} | label9 = [[Video game writer|Writer(s)]] | data9 = {{{writer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P50|qid={{{qid|}}}|name=writer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{writer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P50}}}}}}}}} | label10 = [[Video game composer|Composer(s)]] | data10 = {{{composer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P86|qid={{{qid|}}}|name=composer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{composer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P86}}}}}}}}} | label11 = Series | data11 = {{{series|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P179|qid={{{qid|}}}|name=series|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|prefix=''|postfix=''|{{{series|}}}}}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P179}}}}}}}}} | label12 = [[Game engine|Engine]] | data12 = {{{engine|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P408|qid={{{qid|}}}|name=engine|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{engine|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P408}}}}}}}}} | label13 = [[Computing platform|Platform(s)]] | data13 = {{{platform|{{{platforms|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P400|qid={{{qid|}}}|name=platform|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{platform|{{{platforms|}}}}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P400}}}}}}}}}}}} | label14 = Release | data14 = {{{released|{{{release|}}}}}} | label15 = [[Video game genre|Genre(s)]] | data15 = {{{genre|{{If first display both|{{#invoke:String2 |ucfirst |{{#invoke:WikidataIB|getValue|rank=best|P136|qid={{{qid|}}}|name=genre|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|shortname=yes|{{{genre|}}} }} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P136}}}}}}}}} | label16 = Mode(s) | data16 = {{{modes|{{If first display both|{{#invoke:String2 |ucfirst |{{#invoke:WikidataIB|getValue|rank=best|P404|qid={{{qid|}}}|name=modes|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|shortname=yes|{{{modes|}}} }} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P404}}}}}}}}} | label17 = [[Arcade system board|Arcade system]] | data17 = {{{arcade system|{{If first display both|{{#invoke:WikidataIB|getQualifierValue|P2670|pval=Q631229|qual=P31|qid={{{qid|}}}|name=arcade_system|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{arcade system|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P2670|Q631229}}}}}}}}} <!-- For embedded infoboxes --> | data30 = {{{embedded|}}} | below = <includeonly>{{EditOnWikidata|noicon={{{noicon|no}}}|qid={{{qid|}}}}}</includeonly> <!-- Checking code and closing --> }}{{main other|{{#ifeq:{{lc:{{{italic title|}}}}}|no||{{italic title|force={{#ifeq:{{lc:{{{italic title|}}}}}|force|true}}}}}} }}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox video game with unknown parameters|_VALUE_{{PAGENAME}}]]}}|ignoreblank=1|preview=Page using [[Template:Infobox video game]] with unknown parameter "_VALUE_"| alt | arcade system | artist | caption | border | child | collapsible | commons | composer | designer | developer | director | embedded | engine | fetchwikidata | genre | image | image_size | image_upright | italic title | modes | noicon | onlysourced | platform | platforms | producer | programmer | publisher | qid | refs | release | released | series | state | subbox | suppressfields | title | writer }}<includeonly>{{main other|{{#if:{{safesubst:#invoke:Check for unknown parameters|check|unknown=1|preview=1|embedded|image}}|[[Category:Articles using Infobox video game using locally defined parameters]]|[[Category:Articles with infoboxes completely from Wikidata]]}}}}{{#if:{{{image|}}}|[[Category:Articles using Wikidata infoboxes with locally defined images]]}}</includeonly><noinclude><!-- NOTE: The {{#if:1| ... }} syntax allows for list markup to be included in the data fields --> {{documentation}}<!-- Add cats and interwikis to the /doc subpage, not here! --> </noinclude> 060749360e5c0d0d45f6a711d91d2e0fb8f42a55 Template:Short description 10 411 822 2022-12-23T23:04:16Z wikipedia>Fayenatic london 0 remove categories for several more namespaces, see [[Wikipedia:Categories for discussion/Log/2022 December 15#Pages with short description]] wikitext text/x-wiki {{#ifeq:{{lc:{{{1|}}}}}|none|<nowiki /><!--Prevents whitespace issues when used with adjacent newlines-->|<div class="shortdescription nomobile noexcerpt noprint searchaux" style="display:none">{{{1|}}}{{SHORTDESC:{{{1|}}}|{{{2|}}}}}</div>}}<includeonly>{{#ifeq:{{{pagetype}}}|Disambiguation pages||{{#ifeq:{{pagetype |defaultns = all |user=exclude}}|exclude||{{#ifeq:{{#switch: {{NAMESPACENUMBER}} | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 100 | 101 | 118 | 119 | 828 | 829 | = exclude|#default=}}|exclude||[[Category:{{{pagetype|{{pagetype |defaultns = extended |plural=y}}}}} with short description]]}}}}}}</includeonly><!-- Start tracking -->{{#invoke:Check for unknown parameters|check|unknown={{Main other|[[Category:Pages using short description with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Short description]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | 2 | pagetype | bot |plural }}<!-- -->{{#ifexpr: {{#invoke:String|len|{{{1|}}}}}>100 | [[Category:{{{pagetype|{{pagetype |defaultns = extended |plural=y}}}}} with long short description]]}}<!-- --><includeonly>{{#if:{{{1|}}}||[[Category:Pages with empty short description]]}}</includeonly><!-- -->{{Short description/lowercasecheck|{{{1|}}}}}<!-- -->{{Main other |{{SDcat |sd={{{1|}}} }} }}<noinclude> {{Documentation}} </noinclude> f175a6d61b40a87adb43e2dd4f73c7979759b34c Template:Hlist/styles.css 10 341 677 2022-12-26T18:00:17Z wikipedia>Izno 0 actually remove that block, someone can dig for authorship text text/plain /* {{pp-protected|reason=match parent|small=yes}} */ /* * hlist styles are defined in core and Minerva and differ in Minerva. The * current definitions here (2023-01-01) are sufficient to override Minerva * without use of the hlist-separated class. The most problematic styles were * related to margin, padding, and the bullet. Check files listed at * [[MediaWiki talk:Common.css/to do#hlist-separated]] */ /* * TODO: When the majority of readership supports it (or some beautiful world * in which grade C support is above the minimum threshold), use :is() */ .hlist dl, .hlist ol, .hlist ul { margin: 0; padding: 0; } /* Display list items inline */ .hlist dd, .hlist dt, .hlist li { /* * don't trust the note that says margin doesn't work with inline * removing margin: 0 makes dds have margins again * We also want to reset margin-right in Minerva */ margin: 0; display: inline; } /* Display requested top-level lists inline */ .hlist.inline, .hlist.inline dl, .hlist.inline ol, .hlist.inline ul, /* Display nested lists inline */ .hlist dl dl, .hlist dl ol, .hlist dl ul, .hlist ol dl, .hlist ol ol, .hlist ol ul, .hlist ul dl, .hlist ul ol, .hlist ul ul { display: inline; } /* Hide empty list items */ .hlist .mw-empty-li { display: none; } /* TODO: :not() can maybe be used here to remove the later rule. naive test * seems to work. more testing needed. like so: *.hlist dt:not(:last-child)::after { * content: ": "; *} *.hlist dd:not(:last-child)::after, *.hlist li:not(:last-child)::after { * content: " · "; * font-weight: bold; *} */ /* Generate interpuncts */ .hlist dt::after { content: ": "; } .hlist dd::after, .hlist li::after { content: " · "; font-weight: bold; } .hlist dd:last-child::after, .hlist dt:last-child::after, .hlist li:last-child::after { content: none; } /* Add parentheses around nested lists */ .hlist dd dd:first-child::before, .hlist dd dt:first-child::before, .hlist dd li:first-child::before, .hlist dt dd:first-child::before, .hlist dt dt:first-child::before, .hlist dt li:first-child::before, .hlist li dd:first-child::before, .hlist li dt:first-child::before, .hlist li li:first-child::before { content: " ("; font-weight: normal; } .hlist dd dd:last-child::after, .hlist dd dt:last-child::after, .hlist dd li:last-child::after, .hlist dt dd:last-child::after, .hlist dt dt:last-child::after, .hlist dt li:last-child::after, .hlist li dd:last-child::after, .hlist li dt:last-child::after, .hlist li li:last-child::after { content: ")"; font-weight: normal; } /* Put ordinals in front of ordered list items */ .hlist ol { counter-reset: listitem; } .hlist ol > li { counter-increment: listitem; } .hlist ol > li::before { content: " " counter(listitem) "\a0"; } .hlist dd ol > li:first-child::before, .hlist dt ol > li:first-child::before, .hlist li ol > li:first-child::before { content: " (" counter(listitem) "\a0"; } 8c9dd9c9c00f30eead17fe10f51d183333e81f33 Module:Hlist/styles.css 828 389 778 2022-12-26T18:00:17Z wikipedia>Izno 0 actually remove that block, someone can dig for authorship text text/plain /* {{pp-protected|reason=match parent|small=yes}} */ /* * hlist styles are defined in core and Minerva and differ in Minerva. The * current definitions here (2023-01-01) are sufficient to override Minerva * without use of the hlist-separated class. The most problematic styles were * related to margin, padding, and the bullet. Check files listed at * [[MediaWiki talk:Common.css/to do#hlist-separated]] */ /* * TODO: When the majority of readership supports it (or some beautiful world * in which grade C support is above the minimum threshold), use :is() */ .hlist dl, .hlist ol, .hlist ul { margin: 0; padding: 0; } /* Display list items inline */ .hlist dd, .hlist dt, .hlist li { /* * don't trust the note that says margin doesn't work with inline * removing margin: 0 makes dds have margins again * We also want to reset margin-right in Minerva */ margin: 0; display: inline; } /* Display requested top-level lists inline */ .hlist.inline, .hlist.inline dl, .hlist.inline ol, .hlist.inline ul, /* Display nested lists inline */ .hlist dl dl, .hlist dl ol, .hlist dl ul, .hlist ol dl, .hlist ol ol, .hlist ol ul, .hlist ul dl, .hlist ul ol, .hlist ul ul { display: inline; } /* Hide empty list items */ .hlist .mw-empty-li { display: none; } /* TODO: :not() can maybe be used here to remove the later rule. naive test * seems to work. more testing needed. like so: *.hlist dt:not(:last-child)::after { * content: ": "; *} *.hlist dd:not(:last-child)::after, *.hlist li:not(:last-child)::after { * content: " · "; * font-weight: bold; *} */ /* Generate interpuncts */ .hlist dt::after { content: ": "; } .hlist dd::after, .hlist li::after { content: " · "; font-weight: bold; } .hlist dd:last-child::after, .hlist dt:last-child::after, .hlist li:last-child::after { content: none; } /* Add parentheses around nested lists */ .hlist dd dd:first-child::before, .hlist dd dt:first-child::before, .hlist dd li:first-child::before, .hlist dt dd:first-child::before, .hlist dt dt:first-child::before, .hlist dt li:first-child::before, .hlist li dd:first-child::before, .hlist li dt:first-child::before, .hlist li li:first-child::before { content: " ("; font-weight: normal; } .hlist dd dd:last-child::after, .hlist dd dt:last-child::after, .hlist dd li:last-child::after, .hlist dt dd:last-child::after, .hlist dt dt:last-child::after, .hlist dt li:last-child::after, .hlist li dd:last-child::after, .hlist li dt:last-child::after, .hlist li li:last-child::after { content: ")"; font-weight: normal; } /* Put ordinals in front of ordered list items */ .hlist ol { counter-reset: listitem; } .hlist ol > li { counter-increment: listitem; } .hlist ol > li::before { content: " " counter(listitem) "\a0"; } .hlist dd ol > li:first-child::before, .hlist dt ol > li:first-child::before, .hlist li ol > li:first-child::before { content: " (" counter(listitem) "\a0"; } 8c9dd9c9c00f30eead17fe10f51d183333e81f33 Module:Infobox 828 381 757 2022-12-27T21:29:12Z wikipedia>Izno 0 merge hlist here Scribunto text/plain local p = {} local args = {} local origArgs = {} local root local empty_row_categories = {} local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]' local has_rows = false local lists = { plainlist_t = { patterns = { '^plainlist$', '%splainlist$', '^plainlist%s', '%splainlist%s' }, found = false, styles = 'Plainlist/styles.css' }, hlist_t = { patterns = { '^hlist$', '%shlist$', '^hlist%s', '%shlist%s' }, found = false, styles = 'Hlist/styles.css' } } local function has_list_class(args_to_check) for _, list in pairs(lists) do if not list.found then for _, arg in pairs(args_to_check) do for _, pattern in ipairs(list.patterns) do if mw.ustring.find(arg or '', pattern) then list.found = true break end end if list.found then break end end end end end local function fixChildBoxes(sval, tt) local function notempty( s ) return s and s:match( '%S' ) end if notempty(sval) then local marker = '<span class=special_infobox_marker>' local s = sval -- start moving templatestyles and categories inside of table rows local slast = '' while slast ~= s do slast = s s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1') s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1') end -- end moving templatestyles and categories inside of table rows s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1') s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker) if s:match(marker) then s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '') s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1') s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1') s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1') s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1') s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1') s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1') s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1') s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1') end if s:match(marker) then local subcells = mw.text.split(s, marker) s = '' for k = 1, #subcells do if k == 1 then s = s .. subcells[k] .. '</' .. tt .. '></tr>' elseif k == #subcells then local rowstyle = ' style="display:none"' if notempty(subcells[k]) then rowstyle = '' end s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k] elseif notempty(subcells[k]) then if (k % 2) == 0 then s = s .. subcells[k] else s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>' end end end end -- the next two lines add a newline at the end of lists for the PHP parser -- [[Special:Diff/849054481]] -- remove when [[:phab:T191516]] is fixed or OBE s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n') s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n') s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1') s = mw.ustring.gsub(s, '^(%{%|)', '\n%1') return s else return sval end end -- Cleans empty tables local function cleanInfobox() root = tostring(root) if has_rows == false then root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '') end end -- Returns the union of the values of two tables, as a sequence. local function union(t1, t2) local vals = {} for k, v in pairs(t1) do vals[v] = true end for k, v in pairs(t2) do vals[v] = true end local ret = {} for k, v in pairs(vals) do table.insert(ret, k) end return ret end -- Returns a table containing the numbers of the arguments that exist -- for the specified prefix. For example, if the prefix was 'data', and -- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}. local function getArgNums(prefix) local nums = {} for k, v in pairs(args) do local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$') if num then table.insert(nums, tonumber(num)) end end table.sort(nums) return nums end -- Adds a row to the infobox, with either a header cell -- or a label/data cell combination. local function addRow(rowArgs) if rowArgs.header and rowArgs.header ~= '_BLANK_' then has_rows = true has_list_class({ rowArgs.rowclass, rowArgs.class, args.headerclass }) root :tag('tr') :addClass(rowArgs.rowclass) :cssText(rowArgs.rowstyle) :tag('th') :attr('colspan', '2') :addClass('infobox-header') :addClass(rowArgs.class) :addClass(args.headerclass) -- @deprecated next; target .infobox-<name> .infobox-header :cssText(args.headerstyle) :cssText(rowArgs.rowcellstyle) :wikitext(fixChildBoxes(rowArgs.header, 'th')) if rowArgs.data then root:wikitext( '[[Category:Pages using infobox templates with ignored data cells]]' ) end elseif rowArgs.data and rowArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then has_rows = true has_list_class({ rowArgs.rowclass, rowArgs.class }) local row = root:tag('tr') row:addClass(rowArgs.rowclass) row:cssText(rowArgs.rowstyle) if rowArgs.label then row :tag('th') :attr('scope', 'row') :addClass('infobox-label') -- @deprecated next; target .infobox-<name> .infobox-label :cssText(args.labelstyle) :cssText(rowArgs.rowcellstyle) :wikitext(rowArgs.label) :done() end local dataCell = row:tag('td') dataCell :attr('colspan', not rowArgs.label and '2' or nil) :addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data') :addClass(rowArgs.class) -- @deprecated next; target .infobox-<name> .infobox(-full)-data :cssText(rowArgs.datastyle) :cssText(rowArgs.rowcellstyle) :wikitext(fixChildBoxes(rowArgs.data, 'td')) else table.insert(empty_row_categories, rowArgs.data or '') end end local function renderTitle() if not args.title then return end has_rows = true has_list_class({args.titleclass}) root :tag('caption') :addClass('infobox-title') :addClass(args.titleclass) -- @deprecated next; target .infobox-<name> .infobox-title :cssText(args.titlestyle) :wikitext(args.title) end local function renderAboveRow() if not args.above then return end has_rows = true has_list_class({ args.aboveclass }) root :tag('tr') :tag('th') :attr('colspan', '2') :addClass('infobox-above') :addClass(args.aboveclass) -- @deprecated next; target .infobox-<name> .infobox-above :cssText(args.abovestyle) :wikitext(fixChildBoxes(args.above,'th')) end local function renderBelowRow() if not args.below then return end has_rows = true has_list_class({ args.belowclass }) root :tag('tr') :tag('td') :attr('colspan', '2') :addClass('infobox-below') :addClass(args.belowclass) -- @deprecated next; target .infobox-<name> .infobox-below :cssText(args.belowstyle) :wikitext(fixChildBoxes(args.below,'td')) end local function addSubheaderRow(subheaderArgs) if subheaderArgs.data and subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then has_rows = true has_list_class({ subheaderArgs.rowclass, subheaderArgs.class }) local row = root:tag('tr') row:addClass(subheaderArgs.rowclass) local dataCell = row:tag('td') dataCell :attr('colspan', '2') :addClass('infobox-subheader') :addClass(subheaderArgs.class) :cssText(subheaderArgs.datastyle) :cssText(subheaderArgs.rowcellstyle) :wikitext(fixChildBoxes(subheaderArgs.data, 'td')) else table.insert(empty_row_categories, subheaderArgs.data or '') end end local function renderSubheaders() if args.subheader then args.subheader1 = args.subheader end if args.subheaderrowclass then args.subheaderrowclass1 = args.subheaderrowclass end local subheadernums = getArgNums('subheader') for k, num in ipairs(subheadernums) do addSubheaderRow({ data = args['subheader' .. tostring(num)], -- @deprecated next; target .infobox-<name> .infobox-subheader datastyle = args.subheaderstyle, rowcellstyle = args['subheaderstyle' .. tostring(num)], class = args.subheaderclass, rowclass = args['subheaderrowclass' .. tostring(num)] }) end end local function addImageRow(imageArgs) if imageArgs.data and imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then has_rows = true has_list_class({ imageArgs.rowclass, imageArgs.class }) local row = root:tag('tr') row:addClass(imageArgs.rowclass) local dataCell = row:tag('td') dataCell :attr('colspan', '2') :addClass('infobox-image') :addClass(imageArgs.class) :cssText(imageArgs.datastyle) :wikitext(fixChildBoxes(imageArgs.data, 'td')) else table.insert(empty_row_categories, imageArgs.data or '') end end local function renderImages() if args.image then args.image1 = args.image end if args.caption then args.caption1 = args.caption end local imagenums = getArgNums('image') for k, num in ipairs(imagenums) do local caption = args['caption' .. tostring(num)] local data = mw.html.create():wikitext(args['image' .. tostring(num)]) if caption then data :tag('div') :addClass('infobox-caption') -- @deprecated next; target .infobox-<name> .infobox-caption :cssText(args.captionstyle) :wikitext(caption) end addImageRow({ data = tostring(data), -- @deprecated next; target .infobox-<name> .infobox-image datastyle = args.imagestyle, class = args.imageclass, rowclass = args['imagerowclass' .. tostring(num)] }) end end -- When autoheaders are turned on, preprocesses the rows local function preprocessRows() if not args.autoheaders then return end local rownums = union(getArgNums('header'), getArgNums('data')) table.sort(rownums) local lastheader for k, num in ipairs(rownums) do if args['header' .. tostring(num)] then if lastheader then args['header' .. tostring(lastheader)] = nil end lastheader = num elseif args['data' .. tostring(num)] and args['data' .. tostring(num)]:gsub( category_in_empty_row_pattern, '' ):match('^%S') then local data = args['data' .. tostring(num)] if data:gsub(category_in_empty_row_pattern, ''):match('%S') then lastheader = nil end end end if lastheader then args['header' .. tostring(lastheader)] = nil end end -- Gets the union of the header and data argument numbers, -- and renders them all in order local function renderRows() local rownums = union(getArgNums('header'), getArgNums('data')) table.sort(rownums) for k, num in ipairs(rownums) do addRow({ header = args['header' .. tostring(num)], label = args['label' .. tostring(num)], data = args['data' .. tostring(num)], datastyle = args.datastyle, class = args['class' .. tostring(num)], rowclass = args['rowclass' .. tostring(num)], -- @deprecated next; target .infobox-<name> rowclass rowstyle = args['rowstyle' .. tostring(num)], rowcellstyle = args['rowcellstyle' .. tostring(num)] }) end end local function renderNavBar() if not args.name then return end has_rows = true root :tag('tr') :tag('td') :attr('colspan', '2') :addClass('infobox-navbar') :wikitext(require('Module:Navbar')._navbar{ args.name, mini = 1, }) end local function renderItalicTitle() local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title']) if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then root:wikitext(require('Module:Italic title')._main({})) end end -- Categories in otherwise empty rows are collected in empty_row_categories. -- This function adds them to the module output. It is not affected by -- args.decat because this module should not prevent module-external categories -- from rendering. local function renderEmptyRowCategories() for _, s in ipairs(empty_row_categories) do root:wikitext(s) end end -- Render tracking categories. args.decat == turns off tracking categories. local function renderTrackingCategories() if args.decat == 'yes' then return end if args.child == 'yes' then if args.title then root:wikitext( '[[Category:Pages using embedded infobox templates with the title parameter]]' ) end elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then root:wikitext('[[Category:Articles using infobox templates with no data rows]]') end end --[=[ Loads the templatestyles for the infobox. TODO: FINISH loading base templatestyles here rather than in MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :). When we do this we should clean up the inline CSS below too. Will have to do some bizarre conversion category like with sidebar. ]=] local function loadTemplateStyles() local frame = mw.getCurrentFrame() local hlist_templatestyles = '' if lists.hlist_t.found then hlist_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = lists.hlist_t.styles } } end local plainlist_templatestyles = '' if lists.plainlist_t.found then plainlist_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = lists.plainlist_t.styles } } end -- See function description local base_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' } } local templatestyles = '' if args['templatestyles'] then templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['templatestyles'] } } end local child_templatestyles = '' if args['child templatestyles'] then child_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['child templatestyles'] } } end local grandchild_templatestyles = '' if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['grandchild templatestyles'] } } end return table.concat({ -- hlist -> plainlist -> base is best-effort to preserve old Common.css ordering. -- this ordering is not a guarantee because the rows of interest invoking -- each class may not be on a specific page hlist_templatestyles, plainlist_templatestyles, base_templatestyles, templatestyles, child_templatestyles, grandchild_templatestyles }) end -- common functions between the child and non child cases local function structure_infobox_common() renderSubheaders() renderImages() preprocessRows() renderRows() renderBelowRow() renderNavBar() renderItalicTitle() renderEmptyRowCategories() renderTrackingCategories() cleanInfobox() end -- Specify the overall layout of the infobox, with special settings if the -- infobox is used as a 'child' inside another infobox. local function _infobox() if args.child ~= 'yes' then root = mw.html.create('table') root :addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox') :addClass(args.bodyclass) -- @deprecated next; target .infobox-<name> :cssText(args.bodystyle) has_list_class({ args.bodyclass }) renderTitle() renderAboveRow() else root = mw.html.create() root :wikitext(args.title) end structure_infobox_common() return loadTemplateStyles() .. root end -- If the argument exists and isn't blank, add it to the argument table. -- Blank arguments are treated as nil to match the behaviour of ParserFunctions. local function preprocessSingleArg(argName) if origArgs[argName] and origArgs[argName] ~= '' then args[argName] = origArgs[argName] end end -- Assign the parameters with the given prefixes to the args table, in order, in -- batches of the step size specified. This is to prevent references etc. from -- appearing in the wrong order. The prefixTable should be an array containing -- tables, each of which has two possible fields, a "prefix" string and a -- "depend" table. The function always parses parameters containing the "prefix" -- string, but only parses parameters in the "depend" table if the prefix -- parameter is present and non-blank. local function preprocessArgs(prefixTable, step) if type(prefixTable) ~= 'table' then error("Non-table value detected for the prefix table", 2) end if type(step) ~= 'number' then error("Invalid step value detected", 2) end -- Get arguments without a number suffix, and check for bad input. for i,v in ipairs(prefixTable) do if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then error('Invalid input detected to preprocessArgs prefix table', 2) end preprocessSingleArg(v.prefix) -- Only parse the depend parameter if the prefix parameter is present -- and not blank. if args[v.prefix] and v.depend then for j, dependValue in ipairs(v.depend) do if type(dependValue) ~= 'string' then error('Invalid "depend" parameter value detected in preprocessArgs') end preprocessSingleArg(dependValue) end end end -- Get arguments with number suffixes. local a = 1 -- Counter variable. local moreArgumentsExist = true while moreArgumentsExist == true do moreArgumentsExist = false for i = a, a + step - 1 do for j,v in ipairs(prefixTable) do local prefixArgName = v.prefix .. tostring(i) if origArgs[prefixArgName] then -- Do another loop if any arguments are found, even blank ones. moreArgumentsExist = true preprocessSingleArg(prefixArgName) end -- Process the depend table if the prefix argument is present -- and not blank, or we are processing "prefix1" and "prefix" is -- present and not blank, and if the depend table is present. if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then for j,dependValue in ipairs(v.depend) do local dependArgName = dependValue .. tostring(i) preprocessSingleArg(dependArgName) end end end end a = a + step end end -- Parse the data parameters in the same order that the old {{infobox}} did, so -- that references etc. will display in the expected places. Parameters that -- depend on another parameter are only processed if that parameter is present, -- to avoid phantom references appearing in article reference lists. local function parseDataParameters() preprocessSingleArg('autoheaders') preprocessSingleArg('child') preprocessSingleArg('bodyclass') preprocessSingleArg('subbox') preprocessSingleArg('bodystyle') preprocessSingleArg('title') preprocessSingleArg('titleclass') preprocessSingleArg('titlestyle') preprocessSingleArg('above') preprocessSingleArg('aboveclass') preprocessSingleArg('abovestyle') preprocessArgs({ {prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}} }, 10) preprocessSingleArg('subheaderstyle') preprocessSingleArg('subheaderclass') preprocessArgs({ {prefix = 'image', depend = {'caption', 'imagerowclass'}} }, 10) preprocessSingleArg('captionstyle') preprocessSingleArg('imagestyle') preprocessSingleArg('imageclass') preprocessArgs({ {prefix = 'header'}, {prefix = 'data', depend = {'label'}}, {prefix = 'rowclass'}, {prefix = 'rowstyle'}, {prefix = 'rowcellstyle'}, {prefix = 'class'} }, 50) preprocessSingleArg('headerclass') preprocessSingleArg('headerstyle') preprocessSingleArg('labelstyle') preprocessSingleArg('datastyle') preprocessSingleArg('below') preprocessSingleArg('belowclass') preprocessSingleArg('belowstyle') preprocessSingleArg('name') -- different behaviour for italics if blank or absent args['italic title'] = origArgs['italic title'] preprocessSingleArg('decat') preprocessSingleArg('templatestyles') preprocessSingleArg('child templatestyles') preprocessSingleArg('grandchild templatestyles') end -- If called via #invoke, use the args passed into the invoking template. -- Otherwise, for testing purposes, assume args are being passed directly in. function p.infobox(frame) if frame == mw.getCurrentFrame() then origArgs = frame:getParent().args else origArgs = frame end parseDataParameters() return _infobox() end -- For calling via #invoke within a template function p.infoboxTemplate(frame) origArgs = {} for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end parseDataParameters() return _infobox() end return p 0ddb7e5c8426d67cd589b710efb9912ddfb67fea Module:List 828 352 698 2022-12-29T17:57:56Z wikipedia>Izno 0 add templatestyles for hlist Scribunto text/plain local libUtil = require('libraryUtil') local checkType = libUtil.checkType local mTableTools = require('Module:TableTools') local p = {} local listTypes = { ['bulleted'] = true, ['unbulleted'] = true, ['horizontal'] = true, ['ordered'] = true, ['horizontal_ordered'] = true } function p.makeListData(listType, args) -- Constructs a data table to be passed to p.renderList. local data = {} -- Classes and TemplateStyles data.classes = {} data.templatestyles = '' if listType == 'horizontal' or listType == 'horizontal_ordered' then table.insert(data.classes, 'hlist') data.templatestyles = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Hlist/styles.css' } } elseif listType == 'unbulleted' then table.insert(data.classes, 'plainlist') data.templatestyles = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Plainlist/styles.css' } } end table.insert(data.classes, args.class) -- Main div style data.style = args.style -- Indent for horizontal lists if listType == 'horizontal' or listType == 'horizontal_ordered' then local indent = tonumber(args.indent) indent = indent and indent * 1.6 or 0 if indent > 0 then data.marginLeft = indent .. 'em' end end -- List style types for ordered lists -- This could be "1, 2, 3", "a, b, c", or a number of others. The list style -- type is either set by the "type" attribute or the "list-style-type" CSS -- property. if listType == 'ordered' or listType == 'horizontal_ordered' then data.listStyleType = args.list_style_type or args['list-style-type'] data.type = args['type'] -- Detect invalid type attributes and attempt to convert them to -- list-style-type CSS properties. if data.type and not data.listStyleType and not tostring(data.type):find('^%s*[1AaIi]%s*$') then data.listStyleType = data.type data.type = nil end end -- List tag type if listType == 'ordered' or listType == 'horizontal_ordered' then data.listTag = 'ol' else data.listTag = 'ul' end -- Start number for ordered lists data.start = args.start if listType == 'horizontal_ordered' then -- Apply fix to get start numbers working with horizontal ordered lists. local startNum = tonumber(data.start) if startNum then data.counterReset = 'listitem ' .. tostring(startNum - 1) end end -- List style -- ul_style and ol_style are included for backwards compatibility. No -- distinction is made for ordered or unordered lists. data.listStyle = args.list_style -- List items -- li_style is included for backwards compatibility. item_style was included -- to be easier to understand for non-coders. data.itemStyle = args.item_style or args.li_style data.items = {} for _, num in ipairs(mTableTools.numKeys(args)) do local item = {} item.content = args[num] item.style = args['item' .. tostring(num) .. '_style'] or args['item_style' .. tostring(num)] item.value = args['item' .. tostring(num) .. '_value'] or args['item_value' .. tostring(num)] table.insert(data.items, item) end return data end function p.renderList(data) -- Renders the list HTML. -- Return the blank string if there are no list items. if type(data.items) ~= 'table' or #data.items < 1 then return '' end -- Render the main div tag. local root = mw.html.create('div') for _, class in ipairs(data.classes or {}) do root:addClass(class) end root:css{['margin-left'] = data.marginLeft} if data.style then root:cssText(data.style) end -- Render the list tag. local list = root:tag(data.listTag or 'ul') list :attr{start = data.start, type = data.type} :css{ ['counter-reset'] = data.counterReset, ['list-style-type'] = data.listStyleType } if data.listStyle then list:cssText(data.listStyle) end -- Render the list items for _, t in ipairs(data.items or {}) do local item = list:tag('li') if data.itemStyle then item:cssText(data.itemStyle) end if t.style then item:cssText(t.style) end item :attr{value = t.value} :wikitext(t.content) end return data.templatestyles .. tostring(root) end function p.renderTrackingCategories(args) local isDeprecated = false -- Tracks deprecated parameters. for k, v in pairs(args) do k = tostring(k) if k:find('^item_style%d+$') or k:find('^item_value%d+$') then isDeprecated = true break end end local ret = '' if isDeprecated then ret = ret .. '[[Category:List templates with deprecated parameters]]' end return ret end function p.makeList(listType, args) if not listType or not listTypes[listType] then error(string.format( "bad argument #1 to 'makeList' ('%s' is not a valid list type)", tostring(listType) ), 2) end checkType('makeList', 2, args, 'table') local data = p.makeListData(listType, args) local list = p.renderList(data) local trackingCategories = p.renderTrackingCategories(args) return list .. trackingCategories end for listType in pairs(listTypes) do p[listType] = function (frame) local mArguments = require('Module:Arguments') local origArgs = mArguments.getArgs(frame, { valueFunc = function (key, value) if not value or not mw.ustring.find(value, '%S') then return nil end if mw.ustring.find(value, '^%s*[%*#;:]') then return value else return value:match('^%s*(.-)%s*$') end return nil end }) -- Copy all the arguments to a new table, for faster indexing. local args = {} for k, v in pairs(origArgs) do args[k] = v end return p.makeList(listType, args) end end return p 7a4f36a6e9cd56370bdd8207d23694124821dc1a Module:Navbox/configuration 828 444 888 2022-12-29T18:14:27Z wikipedia>Izno 0 get these in Scribunto text/plain return { aria_label = 'Navbox', nowrap_item = '%s<span class="nowrap">%s</span>', templatestyles = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Module:Navbox/styles.css' } }, hlist_templatestyles = 'Hlist/styles.css', plainlist_templatestyles = 'Plainlist/styles.css', -- do not localize marker table marker = { oddeven = '\127_ODDEVEN_\127', restart = '\127_ODDEVEN0_\127', regex = '\127_ODDEVEN(%d?)_\127' }, category = { orphan = '[[Category:Navbox orphans]]', horizontal_lists = 'Navigational boxes without horizontal lists', background_colors = 'Navboxes using background colours', illegible = 'Potentially illegible navboxes', borders = 'Navboxes using borders', }, keyword = { border_subgroup = 'subgroup', border_child = 'child', border_none = 'none', evenodd_swap = 'swap', navbar_off = 'off', navbar_plain = 'plain', nocat_false = 'false', nowrapitems_yes = 'yes', orphan_yes = 'yes', state_collapsed = 'collapsed', state_off = 'off', state_plain = 'plain', subpage_doc = 'doc', subpage_sandbox = 'sandbox', subpage_testcases = 'testcases', tracking_no = 'no' }, class = { autocollapse = 'autocollapse', collapsible = 'mw-collapsible', collapsed = 'mw-collapsed', -- Warning navbox = 'navbox', -- WMF currently hides 'navbox' from mobile, -- so you probably shouldn't change the navbox class. navbox_abovebelow = 'navbox-abovebelow', navbox_group = 'navbox-group', navbox_image = 'navbox-image', navbox_inner = 'navbox-inner', navbox_list = 'navbox-list', navbox_list_with_group = 'navbox-list-with-group', navbox_part = 'navbox-', -- do not l10n navbox_styles = 'navbox-styles', navbox_subgroup = 'navbox-subgroup', navbox_title = 'navbox-title', -- l10n only if you change pattern.navbox_title below navbox_odd_part = 'odd', -- do not l10n navbox_even_part = 'even', -- do not l10n nomobile = 'nomobile', nowraplinks = 'nowraplinks', noviewer = 'noviewer' -- used to remove images from MediaViewer }, pattern = { listnum = '^list(%d+)$', class = 'class', sandbox = '/sandbox$', navbox = 'Template:Navbox', nowrap = '^<span class="nowrap">', style = 'style$', navbox_title = '<th[^>]*"navbox%-title"', hlist = 'hlist', plainlist = 'plainlist', }, arg = { above = 'above', aboveclass = 'aboveclass', abovestyle = 'abovestyle', basestyle = 'basestyle', bodyclass = 'bodyclass', bodystyle = 'bodystyle', border = 'border', below = 'below', belowclass = 'belowclass', belowstyle = 'belowstyle', evenodd = 'evenodd', evenstyle = 'evenstyle', group1 = 'group1', group2 = 'group2', group_and_num = 'group%d', groupstyle_and_num = 'group%dstyle', groupclass = 'groupclass', groupstyle = 'groupstyle', groupwidth = 'groupwidth', innerstyle = 'innerstyle', image = 'image', imageclass = 'imageclass', imageleft = 'imageleft', imageleftstyle = 'imageleftstyle', imagesetyle = 'imagestyle', list_and_num = 'list%d', listclass_and_num = 'list%dclass', liststyle_and_num = 'list%dstyle', list1padding = 'list1padding', listclass = 'listclass', listpadding = 'listpadding', liststyle = 'liststyle', name = 'name', navbar = 'navbar', navboxclass = 'navboxclass', nocat = 'nocat', nowrapitems = 'nowrapitems', oddstyle = 'oddstyle', orphan = 'orphan', state = 'state', style = 'style', templatestyles = 'templatestyles', child_templatestyles = 'child templatestyles', title = 'title', titleclass = 'titleclass', titlestyle = 'titlestyle', tracking = 'tracking' }, -- names of navbar arguments navbar = { name = 1, fontstyle = 'fontstyle', mini = 'mini' } } 4148736fd32a93636c0413e73ed38afaef065ec9 Module:Navbar/configuration 828 441 882 2022-12-29T18:18:21Z wikipedia>Izno 0 add hlist/styles.css Scribunto text/plain return { ['templatestyles'] = 'Module:Navbar/styles.css', ['hlist_templatestyles'] = 'Hlist/styles.css', ['box_text'] = 'This box: ', -- default text box when not plain or mini ['title_namespace'] = 'Template', -- namespace to default to for title ['invalid_title'] = 'Invalid title ', ['classes'] = { -- set a line to nil if you don't want it ['navbar'] = 'navbar', ['plainlinks'] = 'plainlinks', -- plainlinks ['horizontal_list'] = 'hlist', -- horizontal list class ['mini'] = 'navbar-mini', -- class indicating small links in the navbar ['this_box'] = 'navbar-boxtext', ['brackets'] = 'navbar-brackets', -- 'collapsible' is the key for a class to indicate the navbar is -- setting up the collapsible element in addition to the normal -- navbar. ['collapsible'] = 'navbar-collapse', ['collapsible_title_mini'] = 'navbar-ct-mini', ['collapsible_title_full'] = 'navbar-ct-full' } } b007c336b17ec4bcd4d5a9dca9f8cba301662b55 Module:Navbar 828 440 880 2022-12-29T18:20:02Z wikipedia>Izno 0 add templatestyles for hlist Scribunto text/plain local p = {} local cfg = mw.loadData('Module:Navbar/configuration') local function get_title_arg(is_collapsible, template) local title_arg = 1 if is_collapsible then title_arg = 2 end if template then title_arg = 'template' end return title_arg end local function choose_links(template, args) -- The show table indicates the default displayed items. -- view, talk, edit, hist, move, watch -- TODO: Move to configuration. local show = {true, true, true, false, false, false} if template then show[2] = false show[3] = false local index = {t = 2, d = 2, e = 3, h = 4, m = 5, w = 6, talk = 2, edit = 3, hist = 4, move = 5, watch = 6} -- TODO: Consider removing TableTools dependency. for _, v in ipairs(require ('Module:TableTools').compressSparseArray(args)) do local num = index[v] if num then show[num] = true end end end local remove_edit_link = args.noedit if remove_edit_link then show[3] = false end return show end local function add_link(link_description, ul, is_mini, font_style) local l if link_description.url then l = {'[', '', ']'} else l = {'[[', '|', ']]'} end ul:tag('li') :addClass('nv-' .. link_description.full) :wikitext(l[1] .. link_description.link .. l[2]) :tag(is_mini and 'abbr' or 'span') :attr('title', link_description.html_title) :cssText(font_style) :wikitext(is_mini and link_description.mini or link_description.full) :done() :wikitext(l[3]) :done() end local function make_list(title_text, has_brackets, displayed_links, is_mini, font_style) local title = mw.title.new(mw.text.trim(title_text), cfg.title_namespace) if not title then error(cfg.invalid_title .. title_text) end local talkpage = title.talkPageTitle and title.talkPageTitle.fullText or '' -- TODO: Get link_descriptions and show into the configuration module. -- link_descriptions should be easier... local link_descriptions = { { ['mini'] = 'v', ['full'] = 'view', ['html_title'] = 'View this template', ['link'] = title.fullText, ['url'] = false }, { ['mini'] = 't', ['full'] = 'talk', ['html_title'] = 'Discuss this template', ['link'] = talkpage, ['url'] = false }, { ['mini'] = 'e', ['full'] = 'edit', ['html_title'] = 'Edit this template', ['link'] = title:fullUrl('action=edit'), ['url'] = true }, { ['mini'] = 'h', ['full'] = 'hist', ['html_title'] = 'History of this template', ['link'] = title:fullUrl('action=history'), ['url'] = true }, { ['mini'] = 'm', ['full'] = 'move', ['html_title'] = 'Move this template', ['link'] = mw.title.new('Special:Movepage'):fullUrl('target='..title.fullText), ['url'] = true }, { ['mini'] = 'w', ['full'] = 'watch', ['html_title'] = 'Watch this template', ['link'] = title:fullUrl('action=watch'), ['url'] = true } } local ul = mw.html.create('ul') if has_brackets then ul:addClass(cfg.classes.brackets) :cssText(font_style) end for i, _ in ipairs(displayed_links) do if displayed_links[i] then add_link(link_descriptions[i], ul, is_mini, font_style) end end return ul:done() end function p._navbar(args) -- TODO: We probably don't need both fontstyle and fontcolor... local font_style = args.fontstyle local font_color = args.fontcolor local is_collapsible = args.collapsible local is_mini = args.mini local is_plain = args.plain local collapsible_class = nil if is_collapsible then collapsible_class = cfg.classes.collapsible if not is_plain then is_mini = 1 end if font_color then font_style = (font_style or '') .. '; color: ' .. font_color .. ';' end end local navbar_style = args.style local div = mw.html.create():tag('div') div :addClass(cfg.classes.navbar) :addClass(cfg.classes.plainlinks) :addClass(cfg.classes.horizontal_list) :addClass(collapsible_class) -- we made the determination earlier :cssText(navbar_style) if is_mini then div:addClass(cfg.classes.mini) end local box_text = (args.text or cfg.box_text) .. ' ' -- the concatenated space guarantees the box text is separated if not (is_mini or is_plain) then div :tag('span') :addClass(cfg.classes.box_text) :cssText(font_style) :wikitext(box_text) end local template = args.template local displayed_links = choose_links(template, args) local has_brackets = args.brackets local title_arg = get_title_arg(is_collapsible, template) local title_text = args[title_arg] or (':' .. mw.getCurrentFrame():getParent():getTitle()) local list = make_list(title_text, has_brackets, displayed_links, is_mini, font_style) div:node(list) if is_collapsible then local title_text_class if is_mini then title_text_class = cfg.classes.collapsible_title_mini else title_text_class = cfg.classes.collapsible_title_full end div:done() :tag('div') :addClass(title_text_class) :cssText(font_style) :wikitext(args[1]) end local frame = mw.getCurrentFrame() -- hlist -> navbar is best-effort to preserve old Common.css ordering. return frame:extensionTag{ name = 'templatestyles', args = { src = cfg.hlist_templatestyles } } .. frame:extensionTag{ name = 'templatestyles', args = { src = cfg.templatestyles } } .. tostring(div:done()) end function p.navbar(frame) return p._navbar(require('Module:Arguments').getArgs(frame)) end return p 79f907e59eaa8bbf8dd50bb751933ebeaaa7eb17 Module:Citation/CS1/styles.css 828 403 806 2023-01-14T14:43:34Z wikipedia>Trappist the monk 0 sync from sandbox; text text/plain /* Protection icon the following line controls the page-protection icon in the upper right corner it must remain within this comment {{sandbox other||{{pp-template}}}} */ /* Overrides Some wikis do not override user agent default styles for HTML <cite> and <q>, unlike en.wp. On en.wp, keep these the same as [[MediaWiki:Common.css]]. The word-wrap and :target styles were moved here from Common.css. On en.wp, keep these the same as [[Template:Citation/styles.css]]. */ cite.citation { font-style: inherit; /* Remove italics for <cite> */ /* Break long urls, etc., rather than overflowing box */ word-wrap: break-word; } .citation q { quotes: '"' '"' "'" "'"; /* Straight quote marks for <q> */ } /* Highlight linked elements (such as clicked references) in blue */ .citation:target { /* ignore the linter - all browsers of interest implement this */ background-color: rgba(0, 127, 255, 0.133); } /* ID and URL access Both core and Common.css have selector .mw-parser-output a[href$=".pdf"].external for PDF pages. All TemplateStyles pages are hoisted to .mw-parser-output. We need to have specificity equal to a[href$=".pdf"].external for locks to override PDF icon. That's essentially 2 classes and 1 element. the .id-lock-... selectors are for use by non-citation templates like {{Catalog lookup link}} which do not have to handle PDF links */ .id-lock-free a, .citation .cs1-lock-free a { background: url(//upload.wikimedia.org/wikipedia/commons/6/65/Lock-green.svg) right 0.1em center/9px no-repeat; } .id-lock-limited a, .id-lock-registration a, .citation .cs1-lock-limited a, .citation .cs1-lock-registration a { background: url(//upload.wikimedia.org/wikipedia/commons/d/d6/Lock-gray-alt-2.svg) right 0.1em center/9px no-repeat; } .id-lock-subscription a, .citation .cs1-lock-subscription a { background: url(//upload.wikimedia.org/wikipedia/commons/a/aa/Lock-red-alt-2.svg) right 0.1em center/9px no-repeat; } /* Wikisource Wikisource icon when |chapter= or |title= is wikilinked to Wikisource as in cite wikisource */ .cs1-ws-icon a { background: url(//upload.wikimedia.org/wikipedia/commons/4/4c/Wikisource-logo.svg) right 0.1em center/12px no-repeat; } /* Errors and maintenance */ .cs1-code { /* <code>...</code> style override: mediawiki's css definition is specified here: https://git.wikimedia.org/blob/mediawiki%2Fcore.git/ 69cd73811f7aadd093050dbf20ed70ef0b42a713/skins%2Fcommon%2FcommonElements.css#L199 */ color: inherit; background: inherit; border: none; padding: inherit; } .cs1-hidden-error { display: none; color: #d33; } .cs1-visible-error { color: #d33; } .cs1-maint { display: none; color: #3a3; margin-left: 0.3em; } /* Small text size Set small text size in one place. 0.95 (here) * 0.9 (from references list) is ~0.85, which is the lower bound for size for accessibility. Old styling for this was just 0.85. We could write the rule so that when this template is inside references/reflist, only then does it multiply by 0.95; else multiply by 0.85 */ .cs1-format { font-size: 95%; } /* kerning */ .cs1-kern-left { padding-left: 0.2em; } .cs1-kern-right { padding-right: 0.2em; } /* selflinks – avoid bold font style when cs1|2 template links to the current page */ .citation .mw-selflink { font-weight: inherit; } 7c96feb084b1883e7b6522660da6a14bdcc94752 Module:Citation/CS1/COinS 828 402 804 2023-01-14T14:43:36Z wikipedia>Trappist the monk 0 sync from sandbox; Scribunto text/plain --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local has_accept_as_written, is_set, in_array, remove_wiki_link, strip_apostrophe_markup; -- functions in Module:Citation/CS1/Utilities local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration --[[--------------------------< M A K E _ C O I N S _ T I T L E >---------------------------------------------- Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs) Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't corrupted with strings of %27%27... ]] local function make_coins_title (title, script) title = has_accept_as_written (title); if is_set (title) then title = strip_apostrophe_markup (title); -- strip any apostrophe markup else title = ''; -- if not set, make sure title is an empty string end if is_set (script) then script = script:gsub ('^%l%l%s*:%s*', ''); -- remove language prefix if present (script value may now be empty string) script = strip_apostrophe_markup (script); -- strip any apostrophe markup else script = ''; -- if not set, make sure script is an empty string end if is_set (title) and is_set (script) then script = ' ' .. script; -- add a space before we concatenate end return title .. script; -- return the concatenation end --[[--------------------------< E S C A P E _ L U A _ M A G I C _ C H A R S >---------------------------------- Returns a string where all of Lua's magic characters have been escaped. This is important because functions like string.gsub() treat their pattern and replace strings as patterns, not literal strings. ]] local function escape_lua_magic_chars (argument) argument = argument:gsub("%%", "%%%%"); -- replace % with %% argument = argument:gsub("([%^%$%(%)%.%[%]%*%+%-%?])", "%%%1"); -- replace all other Lua magic pattern characters return argument; end --[[--------------------------< G E T _ C O I N S _ P A G E S >------------------------------------------------ Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS. ]] local function get_coins_pages (pages) local pattern; if not is_set (pages) then return pages; end -- if no page numbers then we're done while true do pattern = pages:match("%[(%w*:?//[^ ]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the URL and following space(s): "[url " if nil == pattern then break; end -- no more URLs pattern = escape_lua_magic_chars (pattern); -- pattern is not a literal string; escape Lua's magic pattern characters pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible end pages = pages:gsub("[%[%]]", ""); -- remove the brackets pages = pages:gsub("–", "-" ); -- replace endashes with hyphens pages = pages:gsub("&%w+;", "-" ); -- and replace HTML entities (&ndash; etc.) with hyphens; do we need to replace numerical entities like &#32; and the like? return pages; end --[=[-------------------------< C O I N S _ R E P L A C E _ M A T H _ S T R I P M A R K E R >------------------ There are three options for math markup rendering that depend on the editor's math preference settings. These settings are at [[Special:Preferences#mw-prefsection-rendering]] and are PNG images TeX source MathML with SVG or PNG fallback All three are heavy with HTML and CSS which doesn't belong in the metadata. Without this function, the metadata saved in the raw wikitext contained the rendering determined by the settings of the last editor to save the page. This function gets the rendered form of an equation according to the editor's preference before the page is saved. It then searches the rendering for the text equivalent of the rendered equation and replaces the rendering with that so that the page is saved without extraneous HTML/CSS markup and with a reasonably readable text form of the equation. When a replacement is made, this function returns true and the value with replacement; otherwise false and the initial value. To replace multipe equations it is necessary to call this function from within a loop. ]=] local function coins_replace_math_stripmarker (value) local stripmarker = cfg.stripmarkers['math']; local rendering = value:match (stripmarker); -- is there a math stripmarker if not rendering then -- when value doesn't have a math stripmarker, abandon this test return false, value; end rendering = mw.text.unstripNoWiki (rendering); -- convert stripmarker into rendered value (or nil? ''? when math render error) if rendering:match ('alt="[^"]+"') then -- if PNG math option rendering = rendering:match ('alt="([^"]+)"'); -- extract just the math text elseif rendering:match ('$%s+.+%s+%$') then -- if TeX math option; $ is legit character that is escapes as \$ rendering = rendering:match ('$%s+(.+)%s+%$') -- extract just the math text elseif rendering:match ('<annotation[^>]+>.+</annotation>') then -- if MathML math option rendering = rendering:match ('<annotation[^>]+>(.+)</annotation>') -- extract just the math text else return false, value; -- had math stripmarker but not one of the three defined forms end return true, value:gsub (stripmarker, rendering, 1); end --[[--------------------------< C O I N S _ C L E A N U P >---------------------------------------------------- Cleanup parameter values for the metadata by removing or replacing invisible characters and certain HTML entities. 2015-12-10: there is a bug in mw.text.unstripNoWiki (). It replaces math stripmarkers with the appropriate content when it shouldn't. See https://phabricator.wikimedia.org/T121085 and Wikipedia_talk:Lua#stripmarkers_and_mw.text.unstripNoWiki.28.29 TODO: move the replacement patterns and replacement values into a table in /Configuration similar to the invisible characters table? ]] local function coins_cleanup (value) local replaced = true; -- default state to get the do loop running while replaced do -- loop until all math stripmarkers replaced replaced, value = coins_replace_math_stripmarker (value); -- replace math stripmarker with text representation of the equation end value = value:gsub (cfg.stripmarkers['math'], "MATH RENDER ERROR"); -- one or more couldn't be replaced; insert vague error message value = mw.text.unstripNoWiki (value); -- replace nowiki stripmarkers with their content value = value:gsub ('<span class="nowrap" style="padding%-left:0%.1em;">&#39;(s?)</span>', "'%1"); -- replace {{'}} or {{'s}} with simple apostrophe or apostrophe-s value = value:gsub ('&nbsp;', ' '); -- replace &nbsp; entity with plain space value = value:gsub ('\226\128\138', ' '); -- replace hair space with plain space if not mw.ustring.find (value, cfg.indic_script) then -- don't remove zero-width joiner characters from indic script value = value:gsub ('&zwj;', ''); -- remove &zwj; entities value = mw.ustring.gsub (value, '[\226\128\141\226\128\139\194\173]', ''); -- remove zero-width joiner, zero-width space, soft hyphen end value = value:gsub ('[\009\010\013 ]+', ' '); -- replace horizontal tab, line feed, carriage return with plain space return value; end --[[--------------------------< C O I N S >-------------------------------------------------------------------- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse the citation information. ]] local function COinS(data, class) if 'table' ~= type(data) or nil == next(data) then return ''; end for k, v in pairs (data) do -- spin through all of the metadata parameter values if 'ID_list' ~= k and 'Authors' ~= k then -- except the ID_list and Author tables (author nowiki stripmarker done when Author table processed) data[k] = coins_cleanup (v); end end local ctx_ver = "Z39.88-2004"; -- treat table strictly as an array with only set values. local OCinSoutput = setmetatable( {}, { __newindex = function(self, key, value) if is_set(value) then rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( remove_wiki_link( value ) ) } ); end end }); if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn', 'journal', 'news', 'magazine'}) or (in_array (class, {'conference', 'interview', 'map', 'press release', 'web'}) and is_set(data.Periodical)) or ('citation' == class and is_set(data.Periodical) and not is_set (data.Encyclopedia)) then OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; -- journal metadata identifier if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) then -- set genre according to the type of citation template we are rendering OCinSoutput["rft.genre"] = "preprint"; -- cite arxiv, cite biorxiv, cite citeseerx, cite ssrn elseif 'conference' == class then OCinSoutput["rft.genre"] = "conference"; -- cite conference (when Periodical set) elseif 'web' == class then OCinSoutput["rft.genre"] = "unknown"; -- cite web (when Periodical set) else OCinSoutput["rft.genre"] = "article"; -- journal and other 'periodical' articles end OCinSoutput["rft.jtitle"] = data.Periodical; -- journal only OCinSoutput["rft.atitle"] = data.Title; -- 'periodical' article titles -- these used only for periodicals OCinSoutput["rft.ssn"] = data.Season; -- keywords: winter, spring, summer, fall OCinSoutput["rft.quarter"] = data.Quarter; -- single digits 1->first quarter, etc. OCinSoutput["rft.chron"] = data.Chron; -- free-form date components OCinSoutput["rft.volume"] = data.Volume; -- does not apply to books OCinSoutput["rft.issue"] = data.Issue; OCinSoutput['rft.artnum'] = data.ArticleNumber; -- {{cite journal}} only OCinSoutput["rft.pages"] = data.Pages; -- also used in book metadata elseif 'thesis' ~= class then -- all others except cite thesis are treated as 'book' metadata; genre distinguishes OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; -- book metadata identifier if 'report' == class or 'techreport' == class then -- cite report and cite techreport OCinSoutput["rft.genre"] = "report"; elseif 'conference' == class then -- cite conference when Periodical not set OCinSoutput["rft.genre"] = "conference"; OCinSoutput["rft.atitle"] = data.Chapter; -- conference paper as chapter in proceedings (book) elseif in_array (class, {'book', 'citation', 'encyclopaedia', 'interview', 'map'}) then if is_set (data.Chapter) then OCinSoutput["rft.genre"] = "bookitem"; OCinSoutput["rft.atitle"] = data.Chapter; -- book chapter, encyclopedia article, interview in a book, or map title else if 'map' == class or 'interview' == class then OCinSoutput["rft.genre"] = 'unknown'; -- standalone map or interview else OCinSoutput["rft.genre"] = 'book'; -- book and encyclopedia end end else -- {'audio-visual', 'AV-media-notes', 'DVD-notes', 'episode', 'interview', 'mailinglist', 'map', 'newsgroup', 'podcast', 'press release', 'serial', 'sign', 'speech', 'web'} OCinSoutput["rft.genre"] = "unknown"; end OCinSoutput["rft.btitle"] = data.Title; -- book only OCinSoutput["rft.place"] = data.PublicationPlace; -- book only OCinSoutput["rft.series"] = data.Series; -- book only OCinSoutput["rft.pages"] = data.Pages; -- book, journal OCinSoutput["rft.edition"] = data.Edition; -- book only OCinSoutput["rft.pub"] = data.PublisherName; -- book and dissertation else -- cite thesis OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:dissertation"; -- dissertation metadata identifier OCinSoutput["rft.title"] = data.Title; -- dissertation (also patent but that is not yet supported) OCinSoutput["rft.degree"] = data.Degree; -- dissertation only OCinSoutput['rft.inst'] = data.PublisherName; -- book and dissertation end -- NB. Not currently supported are "info:ofi/fmt:kev:mtx:patent", "info:ofi/fmt:kev:mtx:dc", "info:ofi/fmt:kev:mtx:sch_svc", "info:ofi/fmt:kev:mtx:ctx" -- and now common parameters (as much as possible) OCinSoutput["rft.date"] = data.Date; -- book, journal, dissertation for k, v in pairs( data.ID_list ) do -- what to do about these? For now assume that they are common to all? if k == 'ISBN' then v = v:gsub( "[^-0-9X]", "" ); end local id = cfg.id_handlers[k].COinS; if string.sub( id or "", 1, 4 ) == 'info' then -- for ids that are in the info:registry OCinSoutput["rft_id"] = table.concat{ id, "/", v }; elseif string.sub (id or "", 1, 3 ) == 'rft' then -- for isbn, issn, eissn, etc. that have defined COinS keywords OCinSoutput[ id ] = v; elseif 'url' == id then -- for urls that are assembled in ~/Identifiers; |asin= and |ol= OCinSoutput["rft_id"] = table.concat ({data.ID_list[k], "#id-name=", cfg.id_handlers[k].label}); elseif id then -- when cfg.id_handlers[k].COinS is not nil so urls created here OCinSoutput["rft_id"] = table.concat{ cfg.id_handlers[k].prefix, v, cfg.id_handlers[k].suffix or '', "#id-name=", cfg.id_handlers[k].label }; -- others; provide a URL and indicate identifier name as #fragment (human-readable, but transparent to browsers) end end local last, first; for k, v in ipairs( data.Authors ) do last, first = coins_cleanup (v.last), coins_cleanup (v.first or ''); -- replace any nowiki stripmarkers, non-printing or invisible characters if k == 1 then -- for the first author name only if is_set(last) and is_set(first) then -- set these COinS values if |first= and |last= specify the first author name OCinSoutput["rft.aulast"] = last; -- book, journal, dissertation OCinSoutput["rft.aufirst"] = first; -- book, journal, dissertation elseif is_set(last) then OCinSoutput["rft.au"] = last; -- book, journal, dissertation -- otherwise use this form for the first name end else -- for all other authors if is_set(last) and is_set(first) then OCinSoutput["rft.au"] = table.concat{ last, ", ", first }; -- book, journal, dissertation elseif is_set(last) then OCinSoutput["rft.au"] = last; -- book, journal, dissertation end -- TODO: At present we do not report "et al.". Add anything special if this condition applies? end end OCinSoutput.rft_id = data.URL; OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage }; -- TODO: Add optional extra info: -- rfr_dat=#REVISION<version> (referrer private data) -- ctx_id=<data.RawPage>#<ref> (identifier for the context object) -- ctx_tim=<ts> (timestamp in format yyyy-mm-ddThh:mm:ssTZD or yyyy-mm-dd) -- ctx_enc=info:ofi/enc:UTF-8 (character encoding) OCinSoutput = setmetatable( OCinSoutput, nil ); -- sort with version string always first, and combine. -- table.sort( OCinSoutput ); table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004" return table.concat(OCinSoutput, "&"); end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr, utilities_page_ptr) cfg = cfg_table_ptr; has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from selected Module:Citation/CS1/Utilities module is_set = utilities_page_ptr.is_set; in_array = utilities_page_ptr.in_array; remove_wiki_link = utilities_page_ptr.remove_wiki_link; strip_apostrophe_markup = utilities_page_ptr.strip_apostrophe_markup; end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return { make_coins_title = make_coins_title, get_coins_pages = get_coins_pages, COinS = COinS, set_selected_modules = set_selected_modules, } 55b7d6a7605b5e672604b0210feeb5286b799f8e Module:Citation/CS1/Identifiers 828 401 802 2023-01-14T14:43:38Z wikipedia>Trappist the monk 0 sync from sandbox; Scribunto text/plain --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local has_accept_as_written, is_set, in_array, set_message, select_one, -- functions in Module:Citation/CS1/Utilities substitute, make_wikilink; local z; -- table of tables defined in Module:Citation/CS1/Utilities local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration --[[--------------------------< P A G E S C O P E V A R I A B L E S >-------------------------------------- declare variables here that have page-wide scope that are not brought in from other modules; that are created here and used here ]] local auto_link_urls = {}; -- holds identifier URLs for those identifiers that can auto-link |title= --============================<< H E L P E R F U N C T I O N S >>============================================ --[[--------------------------< W I K I D A T A _ A R T I C L E _ N A M E _ G E T >---------------------------- as an aid to internationalizing identifier-label wikilinks, gets identifier article names from Wikidata. returns :<lang code>:<article title> when <q> has an <article title> for <lang code>; nil else for identifiers that do not have q, returns nil for wikis that do not have mw.wikibase installed, returns nil ]] local function wikidata_article_name_get (q) if not is_set (q) or (q and not mw.wikibase) then -- when no q number or when a q number but mw.wikibase not installed on this wiki return nil; -- abandon end local wd_article; local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org wd_article = mw.wikibase.getSitelink (q, this_wiki_code .. 'wiki'); -- fetch article title from WD; nil when no title available at this wiki if wd_article then wd_article = table.concat ({':', this_wiki_code, ':', wd_article}); -- interwiki-style link without brackets if taken from WD; leading colon required end return wd_article; -- article title from WD; nil else end --[[--------------------------< L I N K _ L A B E L _ M A K E >------------------------------------------------ common function to create identifier link label from handler table or from Wikidata returns the first available of 1. redirect from local wiki's handler table (if enabled) 2. Wikidata (if there is a Wikidata entry for this identifier in the local wiki's language) 3. label specified in the local wiki's handler table ]] local function link_label_make (handler) local wd_article; if not (cfg.use_identifier_redirects and is_set (handler.redirect)) then -- redirect has priority so if enabled and available don't fetch from Wikidata because expensive wd_article = wikidata_article_name_get (handler.q); -- if Wikidata has an article title for this wiki, get it; end return (cfg.use_identifier_redirects and is_set (handler.redirect) and handler.redirect) or wd_article or handler.link; end --[[--------------------------< E X T E R N A L _ L I N K _ I D >---------------------------------------------- Formats a wiki-style external link ]] local function external_link_id (options) local url_string = options.id; local ext_link; local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org local wd_article; -- article title from Wikidata if options.encode == true or options.encode == nil then url_string = mw.uri.encode (url_string, 'PATH'); end if options.auto_link and is_set (options.access) then auto_link_urls[options.auto_link] = table.concat ({options.prefix, url_string, options.suffix}); end ext_link = mw.ustring.format ('[%s%s%s %s]', options.prefix, url_string, options.suffix or "", mw.text.nowiki (options.id)); if is_set (options.access) then ext_link = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[options.access].class, cfg.presentation[options.access].title, ext_link}); -- add the free-to-read / paywall lock end return table.concat ({ make_wikilink (link_label_make (options), options.label), -- redirect, Wikidata link, or locally specified link (in that order) options.separator or '&nbsp;', ext_link }); end --[[--------------------------< I N T E R N A L _ L I N K _ I D >---------------------------------------------- Formats a wiki-style internal link TODO: Does not currently need to support options.access, options.encode, auto-linking and COinS (as in external_link_id), but may be needed in the future for :m:Interwiki_map custom-prefixes like :arxiv:, :bibcode:, :DOI:, :hdl:, :ISSN:, :JSTOR:, :Openlibrary:, :PMID:, :RFC:. ]] local function internal_link_id (options) local id = mw.ustring.gsub (options.id, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9 return table.concat ( { make_wikilink (link_label_make (options), options.label), -- wiki-link the identifier label options.separator or '&nbsp;', -- add the separator make_wikilink ( table.concat ( { options.prefix, id, -- translated to Western digits options.suffix or '' }), substitute (cfg.presentation['bdi'], {'', mw.text.nowiki (options.id)}) -- bdi tags to prevent Latin script identifiers from being reversed at RTL language wikis ); -- nowiki because MediaWiki still has magic links for ISBN and the like; TODO: is it really required? }); end --[[--------------------------< I S _ E M B A R G O E D >------------------------------------------------------ Determines if a PMC identifier's online version is embargoed. Compares the date in |pmc-embargo-date= against today's date. If embargo date is in the future, returns the content of |pmc-embargo-date=; otherwise, returns an empty string because the embargo has expired or because |pmc-embargo-date= was not set in this cite. ]] local function is_embargoed (embargo) if is_set (embargo) then local lang = mw.getContentLanguage(); local good1, embargo_date, todays_date; good1, embargo_date = pcall (lang.formatDate, lang, 'U', embargo); todays_date = lang:formatDate ('U'); if good1 then -- if embargo date is a good date if tonumber (embargo_date) >= tonumber (todays_date) then -- is embargo date is in the future? return embargo; -- still embargoed else set_message ('maint_pmc_embargo'); -- embargo has expired; add main cat return ''; -- unset because embargo has expired end end end return ''; -- |pmc-embargo-date= not set return empty string end --[=[-------------------------< I S _ V A L I D _ B I O R X I V _ D A T E >------------------------------------ returns true if: 2019-12-11T00:00Z <= biorxiv_date < today + 2 days The dated form of biorxiv identifier has a start date of 2019-12-11. The Unix timestamp for that date is {{#time:U|2019-12-11}} = 1576022400 biorxiv_date is the date provided in those |biorxiv= parameter values that are dated at time 00:00:00 UTC today is the current date at time 00:00:00 UTC plus 48 hours if today is 2015-01-01T00:00:00 then adding 24 hours gives 2015-01-02T00:00:00 – one second more than today adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser apparently doesn't understand non-English date month names. This function will always return false when the date contains a non-English month name because good1 is false after the call to lang_object.formatDate(). To get around that call this function with date parts and create a YYYY-MM-DD format date. ]=] local function is_valid_biorxiv_date (y, m, d) local biorxiv_date = table.concat ({y, m, d}, '-'); -- make ymd date local good1, good2; local biorxiv_ts, tomorrow_ts; -- to hold Unix timestamps representing the dates local lang_object = mw.getContentLanguage(); good1, biorxiv_ts = pcall (lang_object.formatDate, lang_object, 'U', biorxiv_date); -- convert biorxiv_date value to Unix timestamp good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which tonumber() may not understand biorxiv_ts = tonumber (biorxiv_ts) or lang_object:parseFormattedNumber (biorxiv_ts); -- convert to numbers for the comparison; tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts); else return false; -- one or both failed to convert to Unix timestamp end return ((1576022400 <= biorxiv_ts) and (biorxiv_ts < tomorrow_ts)) -- 2012-12-11T00:00Z <= biorxiv_date < tomorrow's date end --[[--------------------------< IS _ V A L I D _ I S X N >----------------------------------------------------- ISBN-10 and ISSN validator code calculates checksum across all ISBN/ISSN digits including the check digit. ISBN-13 is checked in isbn(). If the number is valid the result will be 0. Before calling this function, ISBN/ISSN must be checked for length and stripped of dashes, spaces and other non-ISxN characters. ]] local function is_valid_isxn (isxn_str, len) local temp = 0; isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 len = len + 1; -- adjust to be a loop counter for i, v in ipairs (isxn_str) do -- loop through all of the bytes and calculate the checksum if v == string.byte ("X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) temp = temp + 10 * (len - i); -- it represents 10 decimal else temp = temp + tonumber (string.char (v) )*(len-i); end end return temp % 11 == 0; -- returns true if calculation result is zero end --[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >----------------------------------------------- ISBN-13 and ISMN validator code calculates checksum across all 13 ISBN/ISMN digits including the check digit. If the number is valid, the result will be 0. Before calling this function, ISBN-13/ISMN must be checked for length and stripped of dashes, spaces and other non-ISxN-13 characters. ]] local function is_valid_isxn_13 (isxn_str) local temp=0; isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 for i, v in ipairs (isxn_str) do temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit end return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct end --[[--------------------------< N O R M A L I Z E _ L C C N >-------------------------------------------------- LCCN normalization (https://www.loc.gov/marc/lccn-namespace.html#normalization) 1. Remove all blanks. 2. If there is a forward slash (/) in the string, remove it, and remove all characters to the right of the forward slash. 3. If there is a hyphen in the string: a. Remove it. b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out): 1. All these characters should be digits, and there should be six or less. (not done in this function) 2. If the length of the substring is less than 6, left-fill the substring with zeroes until the length is six. Returns a normalized LCCN for lccn() to validate. There is no error checking (step 3.b.1) performed in this function. ]] local function normalize_lccn (lccn) lccn = lccn:gsub ("%s", ""); -- 1. strip whitespace if nil ~= string.find (lccn, '/') then lccn = lccn:match ("(.-)/"); -- 2. remove forward slash and all character to the right of it end local prefix local suffix prefix, suffix = lccn:match ("(.+)%-(.+)"); -- 3.a remove hyphen by splitting the string into prefix and suffix if nil ~= suffix then -- if there was a hyphen suffix = string.rep("0", 6-string.len (suffix)) .. suffix; -- 3.b.2 left fill the suffix with 0s if suffix length less than 6 lccn = prefix..suffix; -- reassemble the LCCN end return lccn; end --============================<< I D E N T I F I E R F U N C T I O N S >>==================================== --[[--------------------------< A R X I V >-------------------------------------------------------------------- See: https://arxiv.org/help/arxiv_identifier format and error check arXiv identifier. There are three valid forms of the identifier: the first form, valid only between date codes 9107 and 0703, is: arXiv:<archive>.<class>/<date code><number><version> where: <archive> is a string of alpha characters - may be hyphenated; no other punctuation <class> is a string of alpha characters - may be hyphenated; no other punctuation; not the same as |class= parameter which is not supported in this form <date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01 first digit of YY for this form can only 9 and 0 <number> is a three-digit number <version> is a 1 or more digit number preceded with a lowercase v; no spaces (undocumented) the second form, valid from April 2007 through December 2014 is: arXiv:<date code>.<number><version> where: <date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01 <number> is a four-digit number <version> is a 1 or more digit number preceded with a lowercase v; no spaces the third form, valid from January 2015 is: arXiv:<date code>.<number><version> where: <date code> and <version> are as defined for 0704-1412 <number> is a five-digit number ]] local function arxiv (options) local id = options.id; local class = options.Class; -- TODO: lowercase? local handler = options.handler; local year, month, version; local err_msg = false; -- assume no error message local text; -- output text if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9107-0703 format with or without version year, month = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$"); year = tonumber (year); month = tonumber (month); if ((not (90 < year or 8 > year)) or (1 > month or 12 < month)) or -- if invalid year or invalid month ((91 == year and 7 > month) or (7 == year and 3 < month)) then -- if years ok, are starting and ending months ok? err_msg = true; -- flag for error message end elseif id:match("^%d%d[01]%d%.%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%dv%d+$") then -- test for the 0704-1412 with or without version year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$"); year = tonumber (year); month = tonumber (month); if ((7 > year) or (14 < year) or (1 > month or 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years) ((7 == year) and (4 > month)) then -- when year is 07, is month invalid (before April)? err_msg = true; -- flag for error message end elseif id:match("^%d%d[01]%d%.%d%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%d%dv%d+$") then -- test for the 1501- format with or without version year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d%d[v%d]*$"); year = tonumber (year); month = tonumber (month); if ((15 > year) or (1 > month or 12 < month)) then -- is year invalid or is month invalid? (doesn't test for future years) err_msg = true; -- flag for error message end else err_msg = true; -- not a recognized format; flag for error message end if err_msg then options.coins_list_t['ARXIV'] = nil; -- when error, unset so not included in COinS end local err_msg_t = {}; if err_msg then set_message ('err_bad_arxiv'); end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); if is_set (class) then if id:match ('^%d+') then text = table.concat ({text, ' [[//arxiv.org/archive/', class, ' ', class, ']]'}); -- external link within square brackets, not wikilink else set_message ('err_class_ignored'); end end return text; end --[[--------------------------< B I B C O D E >-------------------------------------------------------------------- Validates (sort of) and formats a bibcode ID. Format for bibcodes is specified here: https://adsabs.harvard.edu/abs_doc/help_pages/data.html#bibcodes But, this: 2015arXiv151206696F is apparently valid so apparently, the only things that really matter are length, 19 characters and first four digits must be a year. This function makes these tests: length must be 19 characters characters in position 1–4 must be digits and must represent a year in the range of 1000 – next year 5 must be a letter 6–8 must be letter, digit, ampersand, or dot (ampersand cannot directly precede a dot; &. ) 9–18 must be letter, digit, or dot 19 must be a letter or dot ]] local function bibcode (options) local id = options.id; local access = options.access; local handler = options.handler; local err_type; local err_msg = ''; local year; local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); if 19 ~= id:len() then err_type = cfg.err_msg_supl.length; else year = id:match ("^(%d%d%d%d)[%a][%w&%.][%w&%.][%w&%.][%w.]+[%a%.]$"); if not year then -- if nil then no pattern match err_type = cfg.err_msg_supl.value; -- so value error else local next_year = tonumber (os.date ('%Y')) + 1; -- get the current year as a number and add one for next year year = tonumber (year); -- convert year portion of bibcode to a number if (1000 > year) or (year > next_year) then err_type = cfg.err_msg_supl.year; -- year out of bounds end if id:find('&%.') then err_type = cfg.err_msg_supl.journal; -- journal abbreviation must not have '&.' (if it does it's missing a letter) end end end if is_set (err_type) then -- if there was an error detected set_message ('err_bad_bibcode', {err_type}); options.coins_list_t['BIBCODE'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< B I O R X I V >----------------------------------------------------------------- Format bioRxiv ID and do simple error checking. Before 2019-12-11, biorXiv IDs were 10.1101/ followed by exactly 6 digits. After 2019-12-11, biorXiv IDs retained the six-digit identifier but prefixed that with a yyyy.mm.dd. date and suffixed with an optional version identifier. The bioRxiv ID is the string of characters: https://doi.org/10.1101/078733 -> 10.1101/078733 or a date followed by a six-digit number followed by an optional version indicator 'v' and one or more digits: https://www.biorxiv.org/content/10.1101/2019.12.11.123456v2 -> 10.1101/2019.12.11.123456v2 see https://www.biorxiv.org/about-biorxiv ]] local function biorxiv (options) local id = options.id; local handler = options.handler; local err_msg = true; -- flag; assume that there will be an error local patterns = { '^10.1101/%d%d%d%d%d%d$', -- simple 6-digit identifier (before 2019-12-11) '^10.1101/(20[1-9]%d)%.([01]%d)%.([0-3]%d)%.%d%d%d%d%d%dv%d+$', -- y.m.d. date + 6-digit identifier + version (after 2019-12-11) '^10.1101/(20[1-9]%d)%.([01]%d)%.([0-3]%d)%.%d%d%d%d%d%d$', -- y.m.d. date + 6-digit identifier (after 2019-12-11) } for _, pattern in ipairs (patterns) do -- spin through the patterns looking for a match if id:match (pattern) then local y, m, d = id:match (pattern); -- found a match, attempt to get year, month and date from the identifier if m then -- m is nil when id is the six-digit form if not is_valid_biorxiv_date (y, m, d) then -- validate the encoded date; TODO: don't ignore leap-year and actual month lengths ({{#time:}} is a poor date validator) break; -- date fail; break out early so we don't unset the error message end end err_msg = nil; -- we found a match so unset the error message break; -- and done end end -- err_cat remains set here when no match if err_msg then options.coins_list_t['BIORXIV'] = nil; -- when error, unset so not included in COinS set_message ('err_bad_biorxiv'); -- and set the error message end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); end --[[--------------------------< C I T E S E E R X >------------------------------------------------------------ CiteSeerX use their own notion of "doi" (not to be confused with the identifiers resolved via doi.org). The description of the structure of this identifier can be found at Help_talk:Citation_Style_1/Archive_26#CiteSeerX_id_structure ]] local function citeseerx (options) local id = options.id; local handler = options.handler; local matched; local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); matched = id:match ("^10%.1%.1%.[1-9]%d?%d?%d?%.[1-9]%d?%d?%d?$"); if not matched then set_message ('err_bad_citeseerx' ); options.coins_list_t['CITESEERX'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< D O I >------------------------------------------------------------------------ Formats a DOI and checks for DOI errors. DOI names contain two parts: prefix and suffix separated by a forward slash. Prefix: directory indicator '10.' followed by a registrant code Suffix: character string of any length chosen by the registrant This function checks a DOI name for: prefix/suffix. If the DOI name contains spaces or endashes, or, if it ends with a period or a comma, this function will emit a bad_doi error message. DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash, and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely if ever used in DOI names. https://www.doi.org/doi_handbook/2_Numbering.html -- 2.2 Syntax of a DOI name https://www.doi.org/doi_handbook/2_Numbering.html#2.2.2 -- 2.2.2 DOI prefix ]] local function doi (options) local id = options.id; local inactive = options.DoiBroken local access = options.access; local ignore_invalid = options.accept; local handler = options.handler; local err_flag; local text; if is_set (inactive) then local inactive_year = inactive:match("%d%d%d%d") or ''; -- try to get the year portion from the inactive date local inactive_month, good; if is_set (inactive_year) then if 4 < inactive:len() then -- inactive date has more than just a year (could be anything) local lang_obj = mw.getContentLanguage(); -- get a language object for this wiki good, inactive_month = pcall (lang_obj.formatDate, lang_obj, 'F', inactive); -- try to get the month name from the inactive date if not good then inactive_month = nil; -- something went wrong so make sure this is unset end end else inactive_year = nil; -- |doi-broken-date= has something but it isn't a date end if is_set (inactive_year) and is_set (inactive_month) then set_message ('maint_doi_inactive_dated', {inactive_year, inactive_month, ' '}); elseif is_set (inactive_year) then set_message ('maint_doi_inactive_dated', {inactive_year, '', ''}); else set_message ('maint_doi_inactive'); end inactive = " (" .. cfg.messages['inactive'] .. ' ' .. inactive .. ')'; end local registrant = mw.ustring.match (id, '^10%.([^/]+)/[^%s–]-[^%.,]$'); -- registrant set when DOI has the proper basic form local registrant_err_patterns = { -- these patterns are for code ranges that are not supported '^[^1-3]%d%d%d%d%.%d%d*$', -- 5 digits with subcode (0xxxx, 40000+); accepts: 10000–39999 '^[^1-5]%d%d%d%d$', -- 5 digits without subcode (0xxxx, 60000+); accepts: 10000–59999 '^[^1-9]%d%d%d%.%d%d*$', -- 4 digits with subcode (0xxx); accepts: 1000–9999 '^[^1-9]%d%d%d$', -- 4 digits without subcode (0xxx); accepts: 1000–9999 '^%d%d%d%d%d%d+', -- 6 or more digits '^%d%d?%d?$', -- less than 4 digits without subcode (3 digits with subcode is legitimate) '^%d%d?%.[%d%.]+', -- 1 or 2 digits with subcode '^5555$', -- test registrant will never resolve '[^%d%.]', -- any character that isn't a digit or a dot } if not ignore_invalid then if registrant then -- when DOI has proper form for i, pattern in ipairs (registrant_err_patterns) do -- spin through error patterns if registrant:match (pattern) then -- to validate registrant codes err_flag = set_message ('err_bad_doi'); -- when found, mark this DOI as bad break; -- and done end end else err_flag = set_message ('err_bad_doi'); -- invalid directory or malformed end else set_message ('maint_doi_ignore'); end if err_flag then options.coins_list_t['DOI'] = nil; -- when error, unset so not included in COinS end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access, auto_link = not (err_flag or is_set (inactive) or ignore_invalid) and 'doi' or nil -- do not auto-link when |doi-broken-date= has a value or when there is a DOI error or (to play it safe, after all, auto-linking is not essential) when invalid DOIs are ignored }) .. (inactive or ''); return text; end --[[--------------------------< H D L >------------------------------------------------------------------------ Formats an HDL with minor error checking. HDL names contain two parts: prefix and suffix separated by a forward slash. Prefix: character string using any character in the UCS-2 character set except '/' Suffix: character string of any length using any character in the UCS-2 character set chosen by the registrant This function checks a HDL name for: prefix/suffix. If the HDL name contains spaces, endashes, or, if it ends with a period or a comma, this function will emit a bad_hdl error message. HDL names are case-insensitive and can incorporate any printable Unicode characters so the test for endashes and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely if ever used in HDLs. Query string parameters are named here: https://www.handle.net/proxy_servlet.html. query strings are not displayed but since '?' is an allowed character in an HDL, '?' followed by one of the query parameters is the only way we have to detect the query string so that it isn't URL-encoded with the rest of the identifier. ]] local function hdl (options) local id = options.id; local access = options.access; local handler = options.handler; local query_params = { -- list of known query parameters from https://www.handle.net/proxy_servlet.html 'noredirect', 'ignore_aliases', 'auth', 'cert', 'index', 'type', 'urlappend', 'locatt', 'action', } local hdl, suffix, param = id:match ('(.-)(%?(%a+).+)$'); -- look for query string local found; if hdl then -- when there are query strings, this is the handle identifier portion for _, q in ipairs (query_params) do -- spin through the list of query parameters if param:match ('^' .. q) then -- if the query string begins with one of the parameters found = true; -- announce a find break; -- and stop looking end end end if found then id = hdl; -- found so replace id with the handle portion; this will be URL-encoded, suffix will not else suffix = ''; -- make sure suffix is empty string for concatenation else end local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, suffix = suffix, separator = handler.separator, encode = handler.encode, access = access}) if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma set_message ('err_bad_hdl' ); options.coins_list_t['HDL'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< I S B N >---------------------------------------------------------------------- Determines whether an ISBN string is valid ]] local function isbn (options) local isbn_str = options.id; local ignore_invalid = options.accept; local handler = options.handler; local function return_result (check, err_type) -- local function to handle the various returns local ISBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, prefix = handler.prefix, id = isbn_str, separator = handler.separator}); if ignore_invalid then -- if ignoring ISBN errors set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error else -- here when not ignoring if not check then -- and there is an error options.coins_list_t['ISBN'] = nil; -- when error, unset so not included in COinS set_message ('err_bad_isbn', err_type); -- set an error message return ISBN; -- return id text end end return ISBN; -- return id text end if nil ~= isbn_str:match ('[^%s-0-9X]') then return return_result (false, cfg.err_msg_supl.char); -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X end local id = isbn_str:gsub ('[%s-]', ''); -- remove hyphens and whitespace local len = id:len(); if len ~= 10 and len ~= 13 then return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length end if len == 10 then if id:match ('^%d*X?$') == nil then -- fail if isbn_str has 'X' anywhere but last position return return_result (false, cfg.err_msg_supl.form); end if not is_valid_isxn (id, 10) then -- test isbn-10 for numerical validity return return_result (false, cfg.err_msg_supl.check); -- fail if isbn-10 is not numerically valid end if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin) return return_result (false, cfg.err_msg_supl.group); -- fail if isbn-10 begins with 630/1 end return return_result (true, cfg.err_msg_supl.check); -- pass if isbn-10 is numerically valid else if id:match ('^%d+$') == nil then return return_result (false, cfg.err_msg_supl.char); -- fail if ISBN-13 is not all digits end if id:match ('^97[89]%d*$') == nil then return return_result (false, cfg.err_msg_supl.prefix); -- fail when ISBN-13 does not begin with 978 or 979 end if id:match ('^9790') then return return_result (false, cfg.err_msg_supl.group); -- group identifier '0' is reserved to ISMN end return return_result (is_valid_isxn_13 (id), cfg.err_msg_supl.check); end end --[[--------------------------< A S I N >---------------------------------------------------------------------- Formats a link to Amazon. Do simple error checking: ASIN must be mix of 10 numeric or uppercase alpha characters. If a mix, first character must be uppercase alpha; if all numeric, ASINs must be 10-digit ISBN. If 10-digit ISBN, add a maintenance category so a bot or AWB script can replace |asin= with |isbn=. Error message if not 10 characters, if not ISBN-10, if mixed and first character is a digit. |asin=630....... and |asin=631....... are (apparently) not a legitimate ISBN though it checksums as one; these do not cause this function to emit the maint_asin message This function is positioned here because it calls isbn() ]] local function asin (options) local id = options.id; local domain = options.ASINTLD; local err_flag; if not id:match("^[%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$") then err_flag = set_message ('err_bad_asin'); -- ASIN is not a mix of 10 uppercase alpha and numeric characters else if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X) if is_valid_isxn (id, 10) then -- see if ASIN value is or validates as ISBN-10 if not id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not a valid isbn prefixes but are used by amazon as a numeric identifier err_flag = set_message ('err_bad_asin'); -- ASIN has ISBN-10 form but begins with something other than 630/1 so probably an isbn end elseif not is_set (err_flag) then err_flag = set_message ('err_bad_asin'); -- ASIN is not ISBN-10 end elseif not id:match("^%u[%d%u]+$") then err_flag = set_message ('err_bad_asin'); -- asin doesn't begin with uppercase alpha end end if (not is_set (domain)) or in_array (domain, {'us'}) then -- default: United States domain = "com"; elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom domain = "co." .. domain; elseif in_array (domain, {'z.cn'}) then -- China domain = "cn"; elseif in_array (domain, {'au', 'br', 'mx', 'sg', 'tr'}) then -- Australia, Brazil, Mexico, Singapore, Turkey domain = "com." .. domain; elseif not in_array (domain, {'ae', 'ca', 'cn', 'de', 'es', 'fr', 'in', 'it', 'nl', 'pl', 'sa', 'se', 'co.jp', 'co.uk', 'com', 'com.au', 'com.br', 'com.mx', 'com.sg', 'com.tr'}) then -- Arabic Emirates, Canada, China, Germany, Spain, France, Indonesia, Italy, Netherlands, Poland, Saudi Arabia, Sweden (as of 2021-03 Austria (.at), Liechtenstein (.li) and Switzerland (.ch) still redirect to the German site (.de) with special settings, so don't maintain local ASINs for them) err_flag = set_message ('err_bad_asin_tld'); -- unsupported asin-tld value end local handler = options.handler; if not is_set (err_flag) then options.coins_list_t['ASIN'] = handler.prefix .. domain .. "/dp/" .. id; -- asin for coins else options.coins_list_t['ASIN'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix .. domain .. "/dp/", id = id, encode = handler.encode, separator = handler.separator}) end --[[--------------------------< I S M N >---------------------------------------------------------------------- Determines whether an ISMN string is valid. Similar to ISBN-13, ISMN is 13 digits beginning 979-0-... and uses the same check digit calculations. See https://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf section 2, pages 9–12. ismn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx) or an identifier registered at info-uri.info (info:) ]] local function ismn (options) local id = options.id; local handler = options.handler; local text; local valid_ismn = true; local id_copy; id_copy = id; -- save a copy because this testing is destructive id = id:gsub ('[%s-]', ''); -- remove hyphens and white space if 13 ~= id:len() or id:match ("^9790%d*$" ) == nil then -- ISMN must be 13 digits and begin with 9790 valid_ismn = false; else valid_ismn=is_valid_isxn_13 (id); -- validate ISMN end -- text = internal_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- use this (or external version) when there is some place to link to -- prefix = handler.prefix, id = id_copy, separator = handler.separator, encode = handler.encode}) text = table.concat ( -- because no place to link to yet { make_wikilink (link_label_make (handler), handler.label), handler.separator, id_copy }); if false == valid_ismn then options.coins_list_t['ISMN'] = nil; -- when error, unset so not included in COinS; not really necessary here because ismn not made part of COinS set_message ('err_bad_ismn'); -- create an error message if the ISMN is invalid end return text; end --[[--------------------------< I S S N >---------------------------------------------------------------------- Validate and format an ISSN. This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four digits with a space. When that condition occurred, the resulting link looked like this: |issn=0819 4327 gives: [https://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link This code now prevents that by inserting a hyphen at the ISSN midpoint. It also validates the ISSN for length and makes sure that the checkdigit agrees with the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check ISSN error message. The ISSN is always displayed with a hyphen, even if the ISSN was given as a single group of 8 digits. ]] local function issn (options) local id = options.id; local handler = options.handler; local ignore_invalid = options.accept; local issn_copy = id; -- save a copy of unadulterated ISSN; use this version for display if ISSN does not validate local text; local valid_issn = true; id = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace if 8 ~= id:len() or nil == id:match ("^%d*X?$" ) then -- validate the ISSN: 8 digits long, containing only 0-9 or X in the last position valid_issn = false; -- wrong length or improper character else valid_issn = is_valid_isxn (id, 8); -- validate ISSN end if true == valid_issn then id = string.sub (id, 1, 4 ) .. "-" .. string.sub (id, 5 ); -- if valid, display correctly formatted version else id = issn_copy; -- if not valid, show the invalid ISSN with error message end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) if ignore_invalid then set_message ('maint_issn_ignore'); else if false == valid_issn then options.coins_list_t['ISSN'] = nil; -- when error, unset so not included in COinS set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or ''); -- create an error message if the ISSN is invalid end end return text; end --[[--------------------------< J F M >----------------------------------------------------------------------- A numerical identifier in the form nn.nnnn.nn ]] local function jfm (options) local id = options.id; local handler = options.handler; local id_num; id_num = id:match ('^[Jj][Ff][Mm](.*)$'); -- identifier with jfm prefix; extract identifier if is_set (id_num) then set_message ('maint_jfm_format'); else -- plain number without JFM prefix id_num = id; -- if here id does not have prefix end if id_num and id_num:match('^%d%d%.%d%d%d%d%.%d%d$') then id = id_num; -- jfm matches pattern else set_message ('err_bad_jfm' ); -- set an error message options.coins_list_t['JFM'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< J S T O R >-------------------------------------------------------------------- Format a JSTOR with some error checking ]] local function jstor (options) local id = options.id; local access = options.access; local handler = options.handler; if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then set_message ('err_bad_jstor'); -- set an error message options.coins_list_t['JSTOR'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); end --[[--------------------------< L C C N >---------------------------------------------------------------------- Format LCCN link and do simple error checking. LCCN is a character string 8-12 characters long. The length of the LCCN dictates the character type of the first 1-3 characters; the rightmost eight are always digits. https://oclc-research.github.io/infoURI-Frozen/info-uri.info/info:lccn/reg.html length = 8 then all digits length = 9 then lccn[1] is lowercase alpha length = 10 then lccn[1] and lccn[2] are both lowercase alpha or both digits length = 11 then lccn[1] is lower case alpha, lccn[2] and lccn[3] are both lowercase alpha or both digits length = 12 then lccn[1] and lccn[2] are both lowercase alpha ]] local function lccn (options) local lccn = options.id; local handler = options.handler; local err_flag; -- presume that LCCN is valid local id = lccn; -- local copy of the LCCN id = normalize_lccn (id); -- get canonical form (no whitespace, hyphens, forward slashes) local len = id:len(); -- get the length of the LCCN if 8 == len then if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) err_flag = set_message ('err_bad_lccn'); -- set an error message end elseif 9 == len then -- LCCN should be adddddddd if nil == id:match("%l%d%d%d%d%d%d%d%d") then -- does it match our pattern? err_flag = set_message ('err_bad_lccn'); -- set an error message end elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ... if nil == id:match("^%l%l%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern err_flag = set_message ('err_bad_lccn'); -- no match, set an error message end end elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd if not (id:match("^%l%l%l%d%d%d%d%d%d%d%d") or id:match("^%l%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns err_flag = set_message ('err_bad_lccn'); -- no match, set an error message end elseif 12 == len then -- LCCN should be aadddddddddd if not id:match("^%l%l%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern err_flag = set_message ('err_bad_lccn'); -- no match, set an error message end else err_flag = set_message ('err_bad_lccn'); -- wrong length, set an error message end if not is_set (err_flag) and nil ~= lccn:find ('%s') then err_flag = set_message ('err_bad_lccn'); -- lccn contains a space, set an error message end if is_set (err_flag) then options.coins_list_t['LCCN'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = lccn, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< M R >-------------------------------------------------------------------------- A seven digit number; if not seven digits, zero-fill leading digits to make seven digits. ]] local function mr (options) local id = options.id; local handler = options.handler; local id_num; local id_len; id_num = id:match ('^[Mm][Rr](%d+)$'); -- identifier with mr prefix if is_set (id_num) then set_message ('maint_mr_format'); -- add maint cat else -- plain number without mr prefix id_num = id:match ('^%d+$'); -- if here id is all digits end id_len = id_num and id_num:len() or 0; if (7 >= id_len) and (0 ~= id_len) then id = string.rep ('0', 7-id_len) .. id_num; -- zero-fill leading digits else set_message ('err_bad_mr'); -- set an error message options.coins_list_t['MR'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< O C L C >---------------------------------------------------------------------- Validate and format an OCLC ID. https://www.oclc.org/batchload/controlnumber.en.html {{dead link}} archived at: https://web.archive.org/web/20161228233804/https://www.oclc.org/batchload/controlnumber.en.html ]] local function oclc (options) local id = options.id; local handler = options.handler; local number; if id:match('^ocm%d%d%d%d%d%d%d%d$') then -- ocm prefix and 8 digits; 001 field (12 characters) number = id:match('ocm(%d+)'); -- get the number elseif id:match('^ocn%d%d%d%d%d%d%d%d%d$') then -- ocn prefix and 9 digits; 001 field (12 characters) number = id:match('ocn(%d+)'); -- get the number elseif id:match('^on%d%d%d%d%d%d%d%d%d%d+$') then -- on prefix and 10 or more digits; 001 field (12 characters) number = id:match('^on(%d%d%d%d%d%d%d%d%d%d+)$'); -- get the number elseif id:match('^%(OCoLC%)[1-9]%d*$') then -- (OCoLC) prefix and variable number digits; no leading zeros; 035 field number = id:match('%(OCoLC%)([1-9]%d*)'); -- get the number if 9 < number:len() then number = nil; -- constrain to 1 to 9 digits; change this when OCLC issues 10-digit numbers end elseif id:match('^%d+$') then -- no prefix number = id; -- get the number if 10 < number:len() then number = nil; -- constrain to 1 to 10 digits; change this when OCLC issues 11-digit numbers end end if number then -- proper format id = number; -- exclude prefix, if any, from external link else set_message ('err_bad_oclc') -- add an error message if the id is malformed options.coins_list_t['OCLC'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< O P E N L I B R A R Y >-------------------------------------------------------- Formats an OpenLibrary link, and checks for associated errors. ]] local function openlibrary (options) local id = options.id; local access = options.access; local handler = options.handler; local ident, code = id:gsub('^OL', ''):match("^(%d+([AMW]))$"); -- strip optional OL prefix followed immediately by digits followed by 'A', 'M', or 'W'; local err_flag; local prefix = { -- these are appended to the handler.prefix according to code ['A']='authors/OL', ['M']='books/OL', ['W']='works/OL', ['X']='OL' -- not a code; spoof when 'code' in id is invalid }; if not ident then code = 'X'; -- no code or id completely invalid ident = id; -- copy id to ident so that we display the flawed identifier err_flag = set_message ('err_bad_ol'); end if not is_set (err_flag) then options.coins_list_t['OL'] = handler.prefix .. prefix[code] .. ident; -- experiment for ol coins else options.coins_list_t['OL'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix .. prefix[code], id = ident, separator = handler.separator, encode = handler.encode, access = access}); end --[[--------------------------< O S T I >---------------------------------------------------------------------- Format OSTI and do simple error checking. OSTIs are sequential numbers beginning at 1 and counting up. This code checks the OSTI to see that it contains only digits and is less than test_limit specified in the configuration; the value in test_limit will need to be updated periodically as more OSTIs are issued. NB. 1018 is the lowest OSTI number found in the wild (so far) and resolving OK on the OSTI site ]] local function osti (options) local id = options.id; local access = options.access; local handler = options.handler; if id:match("[^%d]") then -- if OSTI has anything but digits set_message ('err_bad_osti'); -- set an error message options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS else -- OSTI is only digits local id_num = tonumber (id); -- convert id to a number for range testing if 1018 > id_num or handler.id_limit < id_num then -- if OSTI is outside test limit boundaries set_message ('err_bad_osti'); -- set an error message options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS end end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); end --[[--------------------------< P M C >------------------------------------------------------------------------ Format a PMC, do simple error checking, and check for embargoed articles. The embargo parameter takes a date for a value. If the embargo date is in the future the PMC identifier will not be linked to the article. If the embargo date is today or in the past, or if it is empty or omitted, then the PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix. PMC embargo date testing is done in function is_embargoed () which is called earlier because when the citation has |pmc=<value> but does not have a |url= then |title= is linked with the PMC link. Function is_embargoed () returns the embargo date if the PMC article is still embargoed, otherwise it returns an empty string. PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued. ]] local function pmc (options) local id = options.id; local embargo = options.Embargo; -- TODO: lowercase? local handler = options.handler; local err_flag; local id_num; local text; id_num = id:match ('^[Pp][Mm][Cc](%d+)$'); -- identifier with PMC prefix if is_set (id_num) then set_message ('maint_pmc_format'); else -- plain number without PMC prefix id_num = id:match ('^%d+$'); -- if here id is all digits end if is_set (id_num) then -- id_num has a value so test it id_num = tonumber (id_num); -- convert id_num to a number for range testing if 1 > id_num or handler.id_limit < id_num then -- if PMC is outside test limit boundaries err_flag = set_message ('err_bad_pmc'); -- set an error message else id = tostring (id_num); -- make sure id is a string end else -- when id format incorrect err_flag = set_message ('err_bad_pmc'); -- set an error message end if is_set (embargo) and is_set (is_embargoed (embargo)) then -- is PMC is still embargoed? text = table.concat ( -- still embargoed so no external link { make_wikilink (link_label_make (handler), handler.label), handler.separator, id, }); else text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- no embargo date or embargo has expired, ok to link to article prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access, auto_link = not err_flag and 'pmc' or nil -- do not auto-link when PMC has error }); end if err_flag then options.coins_list_t['PMC'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< P M I D >---------------------------------------------------------------------- Format PMID and do simple error checking. PMIDs are sequential numbers beginning at 1 and counting up. This code checks the PMID to see that it contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued. ]] local function pmid (options) local id = options.id; local handler = options.handler; if id:match("[^%d]") then -- if PMID has anything but digits set_message ('err_bad_pmid'); -- set an error message options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS else -- PMID is only digits local id_num = tonumber (id); -- convert id to a number for range testing if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries set_message ('err_bad_pmid'); -- set an error message options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS end end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< R F C >------------------------------------------------------------------------ Format RFC and do simple error checking. RFCs are sequential numbers beginning at 1 and counting up. This code checks the RFC to see that it contains only digits and is less than test_limit specified in the configuration; the value in test_limit will need to be updated periodically as more RFCs are issued. An index of all RFCs is here: https://tools.ietf.org/rfc/ ]] local function rfc (options) local id = options.id; local handler = options.handler; if id:match("[^%d]") then -- if RFC has anything but digits set_message ('err_bad_rfc'); -- set an error message options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS else -- RFC is only digits local id_num = tonumber (id); -- convert id to a number for range testing if 1 > id_num or handler.id_limit < id_num then -- if RFC is outside test limit boundaries set_message ('err_bad_rfc'); -- set an error message options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS end end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); end --[[--------------------------< S 2 C I D >-------------------------------------------------------------------- Format an S2CID, do simple error checking S2CIDs are sequential numbers beginning at 1 and counting up. This code checks the S2CID to see that it is only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more S2CIDs are issued. ]] local function s2cid (options) local id = options.id; local access = options.access; local handler = options.handler; local id_num; local text; id_num = id:match ('^[1-9]%d*$'); -- id must be all digits; must not begin with 0; no open access flag if is_set (id_num) then -- id_num has a value so test it id_num = tonumber (id_num); -- convert id_num to a number for range testing if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries set_message ('err_bad_s2cid'); -- set an error message options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS end else -- when id format incorrect set_message ('err_bad_s2cid'); -- set an error message options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); return text; end --[[--------------------------< S B N >------------------------------------------------------------------------ 9-digit form of ISBN-10; uses same check-digit validation when SBN is prefixed with an additional '0' to make 10 digits sbn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx) or an identifier registered at info-uri.info (info:) ]] local function sbn (options) local id = options.id; local ignore_invalid = options.accept; local handler = options.handler; local function return_result (check, err_type) -- local function to handle the various returns local SBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator}); if not ignore_invalid then -- if not ignoring SBN errors if not check then options.coins_list_t['SBN'] = nil; -- when error, unset so not included in COinS; not really necessary here because sbn not made part of COinS set_message ('err_bad_sbn', {err_type}); -- display an error message return SBN; end else set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error (ToDo: Possibly switch to separate message for SBNs only) end return SBN; end if id:match ('[^%s-0-9X]') then return return_result (false, cfg.err_msg_supl.char); -- fail if SBN contains anything but digits, hyphens, or the uppercase X end local ident = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace; they interfere with the rest of the tests if 9 ~= ident:len() then return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length end if ident:match ('^%d*X?$') == nil then return return_result (false, cfg.err_msg_supl.form); -- fail if SBN has 'X' anywhere but last position end return return_result (is_valid_isxn ('0' .. ident, 10), cfg.err_msg_supl.check); end --[[--------------------------< S S R N >---------------------------------------------------------------------- Format an SSRN, do simple error checking SSRNs are sequential numbers beginning at 100? and counting up. This code checks the SSRN to see that it is only digits and is greater than 99 and less than test_limit; the value in local variable test_limit will need to be updated periodically as more SSRNs are issued. ]] local function ssrn (options) local id = options.id; local handler = options.handler; local id_num; local text; id_num = id:match ('^%d+$'); -- id must be all digits if is_set (id_num) then -- id_num has a value so test it id_num = tonumber (id_num); -- convert id_num to a number for range testing if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries set_message ('err_bad_ssrn'); -- set an error message options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS end else -- when id format incorrect set_message ('err_bad_ssrn'); -- set an error message options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = options.access}); return text; end --[[--------------------------< U S E N E T _ I D >------------------------------------------------------------ Validate and format a usenet message id. Simple error checking, looks for 'id-left@id-right' not enclosed in '<' and/or '>' angle brackets. ]] local function usenet_id (options) local id = options.id; local handler = options.handler; local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) if not id:match('^.+@.+$') or not id:match('^[^<].*[^>]$') then -- doesn't have '@' or has one or first or last character is '< or '>' set_message ('err_bad_usenet_id') -- add an error message if the message id is invalid options.coins_list_t['USENETID'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< Z B L >----------------------------------------------------------------------- A numerical identifier in the form nnnn.nnnnn - leading zeros in the first quartet optional format described here: http://emis.mi.sanu.ac.rs/ZMATH/zmath/en/help/search/ temporary format is apparently eight digits. Anything else is an error ]] local function zbl (options) local id = options.id; local handler = options.handler; if id:match('^%d%d%d%d%d%d%d%d$') then -- is this identifier using temporary format? set_message ('maint_zbl'); -- yes, add maint cat elseif not id:match('^%d?%d?%d?%d%.%d%d%d%d%d$') then -- not temporary, is it normal format? set_message ('err_bad_zbl'); -- no, set an error message options.coins_list_t['ZBL'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --============================<< I N T E R F A C E F U N C T I O N S >>========================================== --[[--------------------------< E X T R A C T _ I D S >------------------------------------------------------------ Populates ID table from arguments using configuration settings. Loops through cfg.id_handlers and searches args for any of the parameters listed in each cfg.id_handlers['...'].parameters. If found, adds the parameter and value to the identifier list. Emits redundant error message if more than one alias exists in args ]] local function extract_ids (args) local id_list = {}; -- list of identifiers found in args for k, v in pairs (cfg.id_handlers) do -- k is uppercase identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table v = select_one (args, v.parameters, 'err_redundant_parameters' ); -- v.parameters is a table of aliases for k; here we pick one from args if present if is_set (v) then id_list[k] = v; end -- if found in args, add identifier to our list end return id_list; end --[[--------------------------< E X T R A C T _ I D _ A C C E S S _ L E V E L S >-------------------------------------- Fetches custom id access levels from arguments using configuration settings. Parameters which have a predefined access level (e.g. arxiv) do not use this function as they are directly rendered as free without using an additional parameter. returns a table of k/v pairs where k is same as the identifier's key in cfg.id_handlers and v is the assigned (valid) keyword access-level values must match the case used in cfg.keywords_lists['id-access'] (lowercase unless there is some special reason for something else) ]] local function extract_id_access_levels (args, id_list) local id_accesses_list = {}; for k, v in pairs (cfg.id_handlers) do local access_param = v.custom_access; -- name of identifier's access-level parameter if is_set (access_param) then local access_level = args[access_param]; -- get the assigned value if there is one if is_set (access_level) then if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required set_message ('err_invalid_param_val', {access_param, access_level}); access_level = nil; -- invalid so unset end if not is_set (id_list[k]) then -- identifier access-level must have a matching identifier set_message ('err_param_access_requires_param', {k:lower()}); -- parameter name is uppercase in cfg.id_handlers (k); lowercase for error message end id_accesses_list[k] = cfg.keywords_xlate[access_level]; -- get translated keyword end end end return id_accesses_list; end --[[--------------------------< B U I L D _ I D _ L I S T >---------------------------------------------------- render the identifiers into a sorted sequence table <ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value <options_t> is a table of various k/v option pairs provided in the call to new_build_id_list(); modified by this function and passed to all identifier rendering functions <access_levels_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid) returns a sequence table of sorted (by hkey - 'handler' key) rendered identifier strings ]] local function build_id_list (ID_list_coins_t, options_t, access_levels_t) local ID_list_t = {}; local accept; local func_map = { --function map points to functions associated with hkey identifier ['ARXIV'] = arxiv, ['ASIN'] = asin, ['BIBCODE'] = bibcode, ['BIORXIV'] = biorxiv, ['CITESEERX'] = citeseerx, ['DOI'] = doi, ['EISSN'] = issn, ['HDL'] = hdl, ['ISBN'] = isbn, ['ISMN'] = ismn, ['ISSN'] = issn, ['JFM'] = jfm, ['JSTOR'] = jstor, ['LCCN'] = lccn, ['MR'] = mr, ['OCLC'] = oclc, ['OL'] = openlibrary, ['OSTI'] = osti, ['PMC'] = pmc, ['PMID'] = pmid, ['RFC'] = rfc, ['S2CID'] = s2cid, ['SBN'] = sbn, ['SSRN'] = ssrn, ['USENETID'] = usenet_id, ['ZBL'] = zbl, } for hkey, v in pairs (ID_list_coins_t) do v, accept = has_accept_as_written (v); -- remove accept-as-written markup if present; accept is boolean true when markup removed; false else -- every function gets the options table with value v and accept boolean options_t.hkey = hkey; -- ~/Configuration handler key options_t.id = v; -- add that identifier value to the options table options_t.accept = accept; -- add the accept boolean flag options_t.access = access_levels_t[hkey]; -- add the access level for those that have an |<identifier-access= parameter options_t.handler = cfg.id_handlers[hkey]; options_t.coins_list_t = ID_list_coins_t; -- pointer to ID_list_coins_t; for |asin= and |ol=; also to keep erroneous values out of the citation's metadata options_t.coins_list_t[hkey] = v; -- id value without accept-as-written markup for metadata if options_t.handler.access and not in_array (options_t.handler.access, cfg.keywords_lists['id-access']) then error (cfg.messages['unknown_ID_access'] .. options_t.handler.access); -- here when handler access key set to a value not listed in list of allowed id access keywords end if func_map[hkey] then local id_text = func_map[hkey] (options_t); -- call the function to get identifier text and any error message table.insert (ID_list_t, {hkey, id_text}); -- add identifier text to the output sequence table else error (cfg.messages['unknown_ID_key'] .. hkey); -- here when func_map doesn't have a function for hkey end end local function comp (a, b) -- used by following table.sort() return a[1]:lower() < b[1]:lower(); -- sort by hkey end table.sort (ID_list_t, comp); -- sequence table of tables sort for k, v in ipairs (ID_list_t) do -- convert sequence table of tables to simple sequence table of strings ID_list_t[k] = v[2]; -- v[2] is the identifier rendering from the call to the various functions in func_map{} end return ID_list_t; end --[[--------------------------< O P T I O N S _ C H E C K >---------------------------------------------------- check that certain option parameters have their associated identifier parameters with values <ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value <ID_support_t> is a sequence table of tables created in citation0() where each subtable has four elements: [1] is the support parameter's assigned value; empty string if not set [2] is a text string same as key in cfg.id_handlers [3] is cfg.error_conditions key used to create error message [4] is original ID support parameter name used to create error message returns nothing; on error emits an appropriate error message ]] local function options_check (ID_list_coins_t, ID_support_t) for _, v in ipairs (ID_support_t) do if is_set (v[1]) and not ID_list_coins_t[v[2]] then -- when support parameter has a value but matching identifier parameter is missing or empty set_message (v[3], (v[4])); -- emit the appropriate error message end end end --[[--------------------------< I D E N T I F I E R _ L I S T S _ G E T >-------------------------------------- Creates two identifier lists: a k/v table of identifiers and their values to be used locally and for use in the COinS metadata, and a sequence table of the rendered identifier strings that will be included in the rendered citation. ]] local function identifier_lists_get (args_t, options_t, ID_support_t) local ID_list_coins_t = extract_ids (args_t); -- get a table of identifiers and their values for use locally and for use in COinS options_check (ID_list_coins_t, ID_support_t); -- ID support parameters must have matching identifier parameters local ID_access_levels_t = extract_id_access_levels (args_t, ID_list_coins_t); -- get a table of identifier access levels local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t); -- get a sequence table of rendered identifier strings return ID_list_t, ID_list_coins_t; -- return the tables end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr, utilities_page_ptr) cfg = cfg_table_ptr; has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from select Module:Citation/CS1/Utilities module is_set = utilities_page_ptr.is_set; in_array = utilities_page_ptr.in_array; set_message = utilities_page_ptr.set_message; select_one = utilities_page_ptr.select_one; substitute = utilities_page_ptr.substitute; make_wikilink = utilities_page_ptr.make_wikilink; z = utilities_page_ptr.z; -- table of tables in Module:Citation/CS1/Utilities end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return { auto_link_urls = auto_link_urls, -- table of identifier URLs to be used when auto-linking |title= identifier_lists_get = identifier_lists_get, -- experiment to replace individual calls to build_id_list(), extract_ids, extract_id_access_levels is_embargoed = is_embargoed; set_selected_modules = set_selected_modules; } 7de1cb3ecf620ae52d26ff9beaf2d8b1c95dedca Module:Citation/CS1/Date validation 828 400 800 2023-01-14T14:43:40Z wikipedia>Trappist the monk 0 sync from sandbox; Scribunto text/plain --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local add_prop_cat, is_set, in_array, set_message, substitute, wrap_style; -- imported functions from selected Module:Citation/CS1/Utilities local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration --[[--------------------------< F I L E - S C O P E D E C L A R A T I O N S >-------------------------------- File-scope variables are declared here ]] local lang_object = mw.getContentLanguage(); -- used by is_valid_accessdate(), is_valid_year(), date_name_xlate(); TODO: move to ~/Configuration? local year_limit; -- used by is_valid_year() --[=[-------------------------< I S _ V A L I D _ A C C E S S D A T E >---------------------------------------- returns true if: Wikipedia start date <= accessdate < today + 2 days Wikipedia start date is 2001-01-15T00:00:00 UTC which is 979516800 seconds after 1970-01-01T00:00:00 UTC (the start of Unix time) accessdate is the date provided in |access-date= at time 00:00:00 UTC today is the current date at time 00:00:00 UTC plus 48 hours if today is 2015-01-01T00:00:00 then adding 24 hours gives 2015-01-02T00:00:00 – one second more than today adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser apparently doesn't understand non-English date month names. This function will always return false when the date contains a non-English month name because good1 is false after the call to lang.formatDate(). To get around that call this function with YYYY-MM-DD format dates. ]=] local function is_valid_accessdate (accessdate) local good1, good2; local access_ts, tomorrow_ts; -- to hold Unix time stamps representing the dates good1, access_ts = pcall (lang_object.formatDate, lang_object, 'U', accessdate ); -- convert accessdate value to Unix timestamp good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand access_ts = tonumber (access_ts) or lang_object:parseFormattedNumber (access_ts); -- convert to numbers for the comparison; tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts); else return false; -- one or both failed to convert to Unix time stamp end if 979516800 <= access_ts and access_ts < tomorrow_ts then -- Wikipedia start date <= accessdate < tomorrow's date return true; else return false; -- accessdate out of range end end --[[--------------------------< G E T _ M O N T H _ N U M B E R >---------------------------------------------- returns a number according to the month in a date: 1 for January, etc. Capitalization and spelling must be correct. If not a valid month, returns 0 ]] local function get_month_number (month) return cfg.date_names['local'].long[month] or cfg.date_names['local'].short[month] or -- look for local names first cfg.date_names['en'].long[month] or cfg.date_names['en'].short[month] or -- failing that, look for English names 0; -- not a recognized month name end --[[--------------------------< G E T _ S E A S O N _ N U M B E R >-------------------------------------------- returns a number according to the sequence of seasons in a year: 21 for Spring, etc. Capitalization and spelling must be correct. If not a valid season, returns 0. 21-24 = Spring, Summer, Autumn, Winter, independent of “Hemisphere” returns 0 when <param> is not |date= Season numbering is defined by Extended Date/Time Format (EDTF) specification (https://www.loc.gov/standards/datetime/) which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using numbers 21-41. cs1|2 only supports generic seasons. EDTF does support the distinction between north and south hemisphere seasons but cs1|2 has no way to make that distinction. These additional divisions not currently supported: 25-28 = Spring - Northern Hemisphere, Summer- Northern Hemisphere, Autumn - Northern Hemisphere, Winter - Northern Hemisphere 29-32 = Spring – Southern Hemisphere, Summer– Southern Hemisphere, Autumn – Southern Hemisphere, Winter - Southern Hemisphere 33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each) 37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each) 40-41 = Semestral 1, Semestral-2 (6 months each) ]] local function get_season_number (season, param) if 'date' ~= param then return 0; -- season dates only supported by |date= end return cfg.date_names['local'].season[season] or -- look for local names first cfg.date_names['en'].season[season] or -- failing that, look for English names 0; -- not a recognized season name end --[[--------------------------< G E T _ Q U A R T E R _ N U M B E R >------------------------------------------ returns a number according to the sequence of quarters in a year: 33 for first quarter, etc. Capitalization and spelling must be correct. If not a valid quarter, returns 0. 33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each) returns 0 when <param> is not |date= Quarter numbering is defined by Extended Date/Time Format (EDTF) specification (https://www.loc.gov/standards/datetime/) which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using numbers 21-41. cs1|2 only supports generic seasons and quarters. These additional divisions not currently supported: 37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each) 40-41 = Semestral 1, Semestral-2 (6 months each) ]] local function get_quarter_number (quarter, param) if 'date' ~= param then return 0; -- quarter dates only supported by |date= end quarter = mw.ustring.gsub (quarter, ' +', ' '); -- special case replace multiple space chars with a single space char return cfg.date_names['local'].quarter[quarter] or -- look for local names first cfg.date_names['en'].quarter[quarter] or -- failing that, look for English names 0; -- not a recognized quarter name end --[[--------------------------< G E T _ P R O P E R _ N A M E _ N U M B E R >---------------------------------- returns a non-zero number if date contains a recognized proper-name. Capitalization and spelling must be correct. returns 0 when <param> is not |date= ]] local function get_proper_name_number (name, param) if 'date' ~= param then return 0; -- proper-name dates only supported by |date= end return cfg.date_names['local'].named[name] or -- look for local names dates first cfg.date_names['en'].named[name] or -- failing that, look for English names 0; -- not a recognized named date end --[[--------------------------< G E T _ E L E M E N T _ N U M B E R <------------------------------------------ returns true if month or season or quarter or proper name is valid (properly spelled, capitalized, abbreviated) ]] local function get_element_number (element, param) local num; local funcs = {get_month_number, get_season_number, get_quarter_number, get_proper_name_number}; -- list of functions to execute in order for _, func in ipairs (funcs) do -- spin through the function list num = func (element, param); -- call the function and get the returned number if 0 ~= num then -- non-zero when valid month season quarter return num; -- return that number end end return nil; -- not valid end --[[--------------------------< I S _ V A L I D _ Y E A R >---------------------------------------------------- Function gets current year from the server and compares it to year from a citation parameter. Years more than one year in the future are not acceptable. Special case for |pmc-embargo-date=: years more than two years in the future are not acceptable ]] local function is_valid_year (year, param) if not is_set (year_limit) then year_limit = tonumber(os.date("%Y"))+1; -- global variable so we only have to fetch it once end year = tonumber (year) or lang_object:parseFormattedNumber (year); -- convert to number for the comparison; if 'pmc-embargo-date' == param then -- special case for |pmc-embargo-date= return year and (year <= tonumber(os.date("%Y"))+2) or false; -- years more than two years in the future are not accepted end return year and (year <= year_limit) or false; end --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- Returns true if day is less than or equal to the number of days in month and year is no farther into the future than next year; else returns false. Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap years before 1582 and Gregorian leap years after 1582. Where the two calendars overlap (1582 to approximately 1923) dates are assumed to be Gregorian. ]] local function is_valid_date (year, month, day, param) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local month_length; if not is_valid_year (year, param) then -- no farther into the future than next year except |pmc-embargo-date= no more than two years in the future return false; end month = tonumber (month); -- required for YYYY-MM-DD dates if (2 == month) then -- if February month_length = 28; -- then 28 days unless if 1582 > tonumber(year) then -- Julian calendar if 0 == (year%4) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end else -- Gregorian calendar if (0 == (year%4) and (0 ~= (year%100) or 0 == (year%400))) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end end else month_length = days_in_month[month]; end if tonumber (day) > month_length then return false; end return true; end --[[--------------------------< I S _ V A L I D _ M O N T H _ R A N G E _ S T Y L E >-------------------------- Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August. This function looks in cfg.date_names{} to see if both month names are listed in the long subtable or both are listed in the short subtable. When both have the same style (both are listed in the same table), returns true; false else ]] local function is_valid_month_range_style (month1, month2) if (cfg.date_names.en.long[month1] and cfg.date_names.en.long[month2]) or -- are both English names listed in the long subtable? (cfg.date_names.en.short[month1] and cfg.date_names.en.short[month2]) or -- are both English names listed in the short subtable? (cfg.date_names['local'].long[month1] and cfg.date_names['local'].long[month2]) or -- are both local names listed in the long subtable? (cfg.date_names['local'].short[month1] and cfg.date_names['local'].short[month2]) then -- are both local names listed in the short subtable? return true; end return false; -- names are mixed end --[[--------------------------< I S _ V A L I D _ M O N T H _ S E A S O N _ R A N G E >------------------------ Check a pair of months or seasons to see if both are valid members of a month or season pair. Month pairs are expected to be left to right, earliest to latest in time. All season ranges are accepted as valid because there are publishers out there who have published a Summer–Spring YYYY issue, hence treat as ok ]] local function is_valid_month_season_range(range_start, range_end, param) local range_start_number = get_month_number (range_start); local range_end_number; if 0 == range_start_number then -- is this a month range? range_start_number = get_season_number (range_start, param); -- not a month; is it a season? get start season number range_end_number = get_season_number (range_end, param); -- get end season number if (0 ~= range_start_number) and (0 ~= range_end_number) and (range_start_number ~= range_end_number) then return true; -- any season pairing is accepted except when both are the same end return false; -- range_start and/or range_end is not a season end -- here when range_start is a month range_end_number = get_month_number (range_end); -- get end month number if range_start_number < range_end_number and -- range_start is a month; does range_start precede range_end? is_valid_month_range_style (range_start, range_end) then -- do months have the same style? return true; -- proper order and same style end return false; -- range_start month number is greater than or equal to range end number; or range end isn't a month end --[[--------------------------< M A K E _ C O I N S _ D A T E >------------------------------------------------ This function receives a table of date parts for one or two dates and an empty table reference declared in Module:Citation/CS1. The function is called only for |date= parameters and only if the |date=<value> is determined to be a valid date format. The question of what to do with invalid date formats is not answered here. The date parts in the input table are converted to an ISO 8601 conforming date string: single whole dates: yyyy-mm-dd month and year dates: yyyy-mm year dates: yyyy ranges: yyyy-mm-dd/yyyy-mm-dd yyyy-mm/yyyy-mm yyyy/yyyy Dates in the Julian calendar are reduced to year or year/year so that we don't have to do calendar conversion from Julian to Proleptic Gregorian. The input table has: year, year2 – always present; if before 1582, ignore months and days if present month, month2 – 0 if not provided, 1-12 for months, 21-24 for seasons; 99 Christmas day, day2 – 0 if not provided, 1-31 for days the output table receives: rftdate: an ISO 8601 formatted date rftchron: a free-form version of the date, usually without year which is in rftdate (season ranges and proper-name dates) rftssn: one of four season keywords: winter, spring, summer, fall (lowercase) rftquarter: one of four values: 1, 2, 3, 4 ]] local function make_COinS_date (input, tCOinS_date) local date; -- one date or first date in a range local date2 = ''; -- end of range date -- start temporary Julian / Gregorian calendar uncertainty detection local year = tonumber(input.year); -- this temporary code to determine the extent of sources dated to the Julian/Gregorian local month = tonumber(input.month); -- interstice 1 October 1582 – 1 January 1926 local day = tonumber (input.day); if (0 ~= day) and -- day must have a value for this to be a whole date (((1582 == year) and (10 <= month) and (12 >= month)) or -- any whole 1582 date from 1 October to 31 December or ((1926 == year) and (1 == month) and (1 == input.day)) or -- 1 January 1926 or ((1582 < year) and (1925 >= year))) then -- any date 1 January 1583 – 31 December 1925 tCOinS_date.inter_cal_cat = true; -- set category flag true end -- end temporary Julian / Gregorian calendar uncertainty detection if 1582 > tonumber(input.year) or 20 < tonumber(input.month) then -- Julian calendar or season so &rft.date gets year only date = input.year; if 0 ~= input.year2 and input.year ~= input.year2 then -- if a range, only the second year portion when not the same as range start year date = string.format ('%.4d/%.4d', tonumber(input.year), tonumber(input.year2)) -- assemble the date range end if 20 < tonumber(input.month) then -- if season or proper-name date local season = {[24] = 'winter', [21] = 'spring', [22] = 'summer', [23] = 'fall', [33] = '1', [34] = '2', [35] = '3', [36] = '4', [98] = 'Easter', [99] = 'Christmas'}; -- seasons lowercase, no autumn; proper-names use title case if 0 == input.month2 then -- single season date if 40 < tonumber(input.month) then tCOinS_date.rftchron = season[input.month]; -- proper-name dates elseif 30 < tonumber(input.month) then tCOinS_date.rftquarter = season[input.month]; -- quarters else tCOinS_date.rftssn = season[input.month]; -- seasons end else -- season range with a second season specified if input.year ~= input.year2 then -- season year – season year range or season year–year tCOinS_date.rftssn = season[input.month]; -- start of range season; keep this? if 0~= input.month2 then tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2); end else -- season–season year range tCOinS_date.rftssn = season[input.month]; -- start of range season; keep this? tCOinS_date.rftchron = season[input.month] .. '–' .. season[input.month2]; -- season–season year range end end end tCOinS_date.rftdate = date; return; -- done end if 0 ~= input.day then date = string.format ('%s-%.2d-%.2d', input.year, tonumber(input.month), tonumber(input.day)); -- whole date elseif 0 ~= input.month then date = string.format ('%s-%.2d', input.year, tonumber(input.month)); -- year and month else date = string.format ('%s', input.year); -- just year end if 0 ~= input.year2 then if 0 ~= input.day2 then date2 = string.format ('/%s-%.2d-%.2d', input.year2, tonumber(input.month2), tonumber(input.day2)); -- whole date elseif 0 ~= input.month2 then date2 = string.format ('/%s-%.2d', input.year2, tonumber(input.month2)); -- year and month else date2 = string.format ('/%s', input.year2); -- just year end end tCOinS_date.rftdate = date .. date2; -- date2 has the '/' separator return; end --[[--------------------------< P A T T E R N S >-------------------------------------------------------------- this is the list of patterns for date formats that this module recognizes. Approximately the first half of these patterns represent formats that might be reformatted into another format. Those that might be reformatted have 'indicator' letters that identify the content of the matching capture: 'd' (day), 'm' (month), 'a' (anchor year), 'y' (year); second day, month, year have a '2' suffix. These patterns are used for both date validation and for reformatting. This table should not be moved to ~/Configuration because changes to this table require changes to check_date() and to reformatter() and reformat_date() ]] local patterns = { -- year-initial numerical year-month-day ['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'}, -- month-initial: month day, year ['Mdy'] = {'^(%D-) +([1-9]%d?), +((%d%d%d%d?)%a?)$', 'm', 'd', 'a', 'y'}, -- month-initial day range: month day–day, year; days are separated by endash ['Md-dy'] = {'^(%D-) +([1-9]%d?)[%-–]([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'd2', 'a', 'y'}, -- day-initial: day month year ['dMy'] = {'^([1-9]%d?) +(%D-) +((%d%d%d%d?)%a?)$', 'd', 'm', 'a', 'y'}, -- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed; not supported at en.wiki -- ['yMd'] = {'^((%d%d%d%d?)%a?) +(%D-) +(%d%d?)$', 'a', 'y', 'm', 'd'}, -- day-range-initial: day–day month year; days are separated by endash ['d-dMy'] = {'^([1-9]%d?)[%-–]([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'd2', 'm', 'a', 'y'}, -- day initial month-day-range: day month - day month year; uses spaced endash ['dM-dMy'] = {'^([1-9]%d?) +(%D-) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'd2', 'm2', 'a', 'y'}, -- month initial month-day-range: month day – month day, year; uses spaced endash ['Md-Mdy'] = {'^(%D-) +([1-9]%d?) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$','m', 'd', 'm2', 'd2', 'a', 'y'}, -- day initial month-day-year-range: day month year - day month year; uses spaced endash ['dMy-dMy'] = {'^([1-9]%d?) +(%D-) +(%d%d%d%d) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'y', 'd2', 'm2', 'a', 'y2'}, -- month initial month-day-year-range: month day, year – month day, year; uses spaced endash ['Mdy-Mdy'] = {'^(%D-) +([1-9]%d?), +(%d%d%d%d) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'y', 'm2', 'd2', 'a', 'y2'}, -- these date formats cannot be converted, per se, but month name can be rendered short or long -- month/season year - month/season year; separated by spaced endash ['My-My'] = {'^(%D-) +(%d%d%d%d) +[%-–] +(%D-) +((%d%d%d%d)%a?)$', 'm', 'y', 'm2', 'a', 'y2'}, -- month/season range year; months separated by endash ['M-My'] = {'^(%D-)[%-–](%D-) +((%d%d%d%d)%a?)$', 'm', 'm2', 'a', 'y'}, -- month/season year or proper-name year; quarter year when First Quarter YYYY etc. ['My'] = {'^([^%d–]-) +((%d%d%d%d)%a?)$', 'm', 'a', 'y'}, -- this way because endash is a member of %D; %D- will match January–March 2019 when it shouldn't -- these date formats cannot be converted ['Sy4-y2'] = {'^(%D-) +((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash ['Sy-y'] = {'^(%D-) +(%d%d%d%d)[%-–]((%d%d%d%d)%a?)$'}, -- special case Winter/Summer year-year; year separated with unspaced endash ['y-y'] = {'^(%d%d%d%d?)[%-–]((%d%d%d%d?)%a?)$'}, -- year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999 ['y4-y2'] = {'^((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- year range: YYYY–YY; separated by unspaced endash ['y'] = {'^((%d%d%d%d?)%a?)$'}, -- year; here accept either YYY or YYYY } --[[--------------------------< I S _ V A L I D _ E M B A R G O _ D A T E >------------------------------------ returns true and date value if that value has proper dmy, mdy, ymd format. returns false and 9999 (embargoed forever) when date value is not proper format; assumes that when |pmc-embargo-date= is set, the editor intended to embargo a PMC but |pmc-embargo-date= does not hold a single date. ]] local function is_valid_embargo_date (v) if v:match (patterns['ymd'][1]) or -- ymd v:match (patterns['Mdy'][1]) or -- dmy v:match (patterns['dMy'][1]) then -- mdy return true, v; end return false, '9999'; -- if here not good date so return false and set embargo date to long time in future end --[[--------------------------< C H E C K _ D A T E >---------------------------------------------------------- Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only allowed range separator is endash. Additionally, check the date to see that it is a real date: no 31 in 30-day months; no 29 February when not a leap year. Months, both long-form and three character abbreviations, and seasons must be spelled correctly. Future years beyond next year are not allowed. If the date fails the format tests, this function returns false and does not return values for anchor_year and COinS_date. When this happens, the date parameter is (DEBUG: not?) used in the COinS metadata and the CITEREF identifier gets its year from the year parameter if present otherwise CITEREF does not get a date value. Inputs: date_string - date string from date-holding parameters (date, year, publication-date, access-date, pmc-embargo-date, archive-date, lay-date) Returns: false if date string is not a real date; else true, anchor_year, COinS_date anchor_year can be used in CITEREF anchors COinS_date is ISO 8601 format date; see make_COInS_date() ]] local function check_date (date_string, param, tCOinS_date) local year; -- assume that year2, months, and days are not used; local year2 = 0; -- second year in a year range local month = 0; local month2 = 0; -- second month in a month range local day = 0; local day2 = 0; -- second day in a day range local anchor_year; local coins_date; if date_string:match (patterns['ymd'][1]) then -- year-initial numerical year month day format year, month, day = date_string:match (patterns['ymd'][1]); if 12 < tonumber(month) or 1 > tonumber(month) or 1582 > tonumber(year) or 0 == tonumber(day) then return false; end -- month or day number not valid or not Gregorian calendar anchor_year = year; elseif mw.ustring.match(date_string, patterns['Mdy'][1]) then -- month-initial: month day, year month, day, anchor_year, year = mw.ustring.match(date_string, patterns['Mdy'][1]); month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months elseif mw.ustring.match(date_string, patterns['Md-dy'][1]) then -- month-initial day range: month day–day, year; days are separated by endash month, day, day2, anchor_year, year = mw.ustring.match(date_string, patterns['Md-dy'][1]); if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same; month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months month2=month; -- for metadata year2 = year; elseif mw.ustring.match(date_string, patterns['dMy'][1]) then -- day-initial: day month year day, month, anchor_year, year = mw.ustring.match(date_string, patterns['dMy'][1]); month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months --[[ NOT supported at en.wiki elseif mw.ustring.match(date_string, patterns['yMd'][1]) then -- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed anchor_year, year, month, day = mw.ustring.match(date_string, patterns['yMd'][1]); month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months -- end NOT supported at en.wiki ]] elseif mw.ustring.match(date_string, patterns['d-dMy'][1]) then -- day-range-initial: day–day month year; days are separated by endash day, day2, month, anchor_year, year = mw.ustring.match(date_string, patterns['d-dMy'][1]); if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same; month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months month2 = month; -- for metadata year2 = year; elseif mw.ustring.match(date_string, patterns['dM-dMy'][1]) then -- day initial month-day-range: day month - day month year; uses spaced endash day, month, day2, month2, anchor_year, year = mw.ustring.match(date_string, patterns['dM-dMy'][1]); if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end -- date range order is left to right: earlier to later; month = get_month_number (month); -- for metadata month2 = get_month_number (month2); year2 = year; elseif mw.ustring.match(date_string, patterns['Md-Mdy'][1]) then -- month initial month-day-range: month day – month day, year; uses spaced endash month, day, month2, day2, anchor_year, year = mw.ustring.match(date_string, patterns['Md-Mdy'][1]); if (not is_valid_month_season_range(month, month2, param)) or not is_valid_year(year) then return false; end month = get_month_number (month); -- for metadata month2 = get_month_number (month2); year2 = year; elseif mw.ustring.match(date_string, patterns['dMy-dMy'][1]) then -- day initial month-day-year-range: day month year - day month year; uses spaced endash day, month, year, day2, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['dMy-dMy'][1]); if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style month = get_month_number (month); -- for metadata month2 = get_month_number (month2); if 0 == month or 0 == month2 then return false; end -- both must be valid elseif mw.ustring.match(date_string, patterns['Mdy-Mdy'][1]) then -- month initial month-day-year-range: month day, year – month day, year; uses spaced endash month, day, year, month2, day2, anchor_year, year2 = mw.ustring.match(date_string, patterns['Mdy-Mdy'][1]); if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style month = get_month_number (month); -- for metadata month2 = get_month_number(month2); if 0 == month or 0 == month2 then return false; end -- both must be valid elseif mw.ustring.match(date_string, patterns['Sy4-y2'][1]) then -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash local century; month, year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy4-y2'][1]); if 'Winter' ~= month and 'Summer' ~= month then return false end; -- 'month' can only be Winter or Summer anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years year2 = century..year2; -- add the century to year2 for comparisons if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) then return false; end -- no year farther in the future than next year month = get_season_number(month, param); elseif mw.ustring.match(date_string, patterns['Sy-y'][1]) then -- special case Winter/Summer year-year; year separated with unspaced endash month, year, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy-y'][1]); month = get_season_number (month, param); -- <month> can only be winter or summer; also for metadata if (month ~= cfg.date_names['en'].season['Winter']) and (month ~= cfg.date_names['en'].season['Summer']) then return false; -- not Summer or Winter; abandon end anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) then return false; end -- no year farther in the future than next year elseif mw.ustring.match(date_string, patterns['My-My'][1]) then -- month/season year - month/season year; separated by spaced endash month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['My-My'][1]); anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same if not is_valid_year(year2) then return false; end -- no year farther in the future than next year if 0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2) then -- both must be month year, same month style month = get_month_number(month); month2 = get_month_number(month2); elseif 0 ~= get_season_number(month, param) and 0 ~= get_season_number(month2, param) then -- both must be season year, not mixed month = get_season_number(month, param); month2 = get_season_number(month2, param); else return false; end elseif mw.ustring.match(date_string, patterns['M-My'][1]) then -- month/season range year; months separated by endash month, month2, anchor_year, year = mw.ustring.match(date_string, patterns['M-My'][1]); if (not is_valid_month_season_range(month, month2, param)) or (not is_valid_year(year)) then return false; end if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season month = get_month_number(month); month2 = get_month_number(month2); if 0 == month or 0 == month2 then return false; end else month = get_season_number(month, param); month2 = get_season_number(month2, param); end year2 = year; elseif mw.ustring.match(date_string, patterns['My'][1]) then -- month/season/quarter/proper-name year month, anchor_year, year = mw.ustring.match(date_string, patterns['My'][1]); if not is_valid_year(year) then return false; end month = get_element_number(month, param); -- get month season quarter proper-name number or nil if not month then return false; end -- not valid whatever it is elseif mw.ustring.match(date_string, patterns['y-y'][1]) then -- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999 year, anchor_year, year2 = mw.ustring.match(date_string, patterns['y-y'][1]); anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same if not is_valid_year(year2) then return false; end -- no year farther in the future than next year elseif mw.ustring.match(date_string, patterns['y4-y2'][1]) then -- Year range: YYYY–YY; separated by unspaced endash local century; year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['y4-y2'][1]); anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years if in_array (param, {'date', 'publication-date', 'year'}) then add_prop_cat ('year-range-abbreviated'); end if 13 > tonumber(year2) then return false; end -- don't allow 2003-05 which might be May 2003 year2 = century .. year2; -- add the century to year2 for comparisons if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same if not is_valid_year(year2) then return false; end -- no year farther in the future than next year elseif mw.ustring.match(date_string, patterns['y'][1]) then -- year; here accept either YYY or YYYY anchor_year, year = mw.ustring.match(date_string, patterns['y'][1]); if false == is_valid_year(year) then return false; end else return false; -- date format not one of the MOS:DATE approved formats end if param ~= 'date' then -- CITEREF disambiguation only allowed in |date=; |year= & |publication-date= promote to date if anchor_year:match ('%l$') then return false; end end if 'access-date' == param then -- test accessdate here because we have numerical date parts if 0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required 0 == year2 and 0 == month2 and 0 == day2 then -- none of these; accessdate must not be a range if not is_valid_accessdate(year .. '-' .. month .. '-' .. day) then return false; -- return false when accessdate out of bounds end else return false; -- return false when accessdate is a range of two dates end end local result=true; -- check whole dates for validity; assume true because not all dates will go through this test if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then -- YMD (simple whole date) result = is_valid_date (year, month, day, param); -- <param> for |pmc-embargo-date= elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then -- YMD-d (day range) result = is_valid_date (year, month, day); result = result and is_valid_date (year, month, day2); elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-md (day month range) result = is_valid_date (year, month, day); result = result and is_valid_date (year, month2, day2); elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 ~= year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-ymd (day month year range) result = is_valid_date(year, month, day); result = result and is_valid_date(year2, month2, day2); end if false == result then return false; end if nil ~= tCOinS_date then -- this table only passed into this function when testing |date= parameter values make_COinS_date ({year = year, month = month, day = day, year2 = year2, month2 = month2, day2 = day2}, tCOinS_date); -- make an ISO 8601 date string for COinS end return true, anchor_year; -- format is good and date string represents a real date end --[[--------------------------< D A T E S >-------------------------------------------------------------------- Cycle the date-holding parameters in passed table date_parameters_list through check_date() to check compliance with MOS:DATE. For all valid dates, check_date() returns true. The |date= parameter test is unique, it is the only date holding parameter from which values for anchor_year (used in CITEREF identifiers) and COinS_date (used in the COinS metadata) are derived. The |date= parameter is the only date-holding parameter that is allowed to contain the no-date keywords "n.d." or "nd" (without quotes). Unlike most error messages created in this module, only one error message is created by this function. Because all of the date holding parameters are processed serially, parameters with errors are added to the <error_list> sequence table as the dates are tested. ]] local function dates(date_parameters_list, tCOinS_date, error_list) local anchor_year; -- will return as nil if the date being tested is not |date= local COinS_date; -- will return as nil if the date being tested is not |date= local embargo_date; -- if embargo date is a good dmy, mdy, ymd date then holds original value else reset to 9999 local good_date = false; for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list if is_set(v.val) then -- if the parameter has a value v.val = mw.ustring.gsub(v.val, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9 if v.val:match("^c%. [1-9]%d%d%d?%a?$") then -- special case for c. year or with or without CITEREF disambiguator - only |date= and |year= local year = v.val:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested if 'date' == k then anchor_year, COinS_date = v.val:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter good_date = is_valid_year(year); elseif 'year' == k then good_date = is_valid_year(year); end elseif 'date' == k then -- if the parameter is |date= if v.val:match("^n%.d%.%a?$") then -- ToDo: I18N -- if |date=n.d. with or without a CITEREF disambiguator good_date, anchor_year, COinS_date = true, v.val:match("((n%.d%.)%a?)"); -- ToDo: I18N -- "n.d."; no error when date parameter is set to no date elseif v.val:match("^nd%a?$") then -- ToDo: I18N -- if |date=nd with or without a CITEREF disambiguator good_date, anchor_year, COinS_date = true, v.val:match("((nd)%a?)"); -- ToDo: I18N -- "nd"; no error when date parameter is set to no date else good_date, anchor_year, COinS_date = check_date (v.val, k, tCOinS_date); -- go test the date end elseif 'year' == k then -- if the parameter is |year= it should hold only a year value if v.val:match("^[1-9]%d%d%d?%a?$") then -- if |year = 3 or 4 digits only with or without a CITEREF disambiguator good_date, anchor_year, COinS_date = true, v.val:match("((%d+)%a?)"); end elseif 'pmc-embargo-date' == k then -- if the parameter is |pmc-embargo-date= good_date = check_date (v.val, k); -- go test the date if true == good_date then -- if the date is a valid date good_date, embargo_date = is_valid_embargo_date (v.val); -- is |pmc-embargo-date= date a single dmy, mdy, or ymd formatted date? yes: returns embargo date; no: returns 9999 end else -- any other date-holding parameter good_date = check_date (v.val, k); -- go test the date end if false == good_date then -- assemble one error message so we don't add the tracking category multiple times table.insert (error_list, wrap_style ('parameter', v.name)); -- make parameter name suitable for error message list end end end return anchor_year, embargo_date; -- and done end --[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------ Compare the value provided in |year= with the year value(s) provided in |date=. This function sets a local numeric value: 0 - year value does not match the year value in date 1 - (default) year value matches the year value in date or one of the year values when date contains two years 2 - year value matches the year value in date when date is in the form YYYY-MM-DD and year is disambiguated (|year=YYYYx) the numeric value in <result> determines the 'output' if any from this function: 0 – adds error message to error_list sequence table 1 – adds maint cat 2 – does nothing ]] local function year_date_check (year_string, year_origin, date_string, date_origin, error_list) local year; local date1; local date2; local result = 1; -- result of the test; assume that the test passes year = year_string:match ('(%d%d%d%d?)'); if date_string:match ('%d%d%d%d%-%d%d%-%d%d') and year_string:match ('%d%d%d%d%a') then --special case where both date and year are required YYYY-MM-DD and YYYYx date1 = date_string:match ('(%d%d%d%d)'); year = year_string:match ('(%d%d%d%d)'); if year ~= date1 then result = 0; -- years don't match else result = 2; -- years match; but because disambiguated, don't add to maint cat end elseif date_string:match ("%d%d%d%d?.-%d%d%d%d?") then -- any of the standard range formats of date with two three- or four-digit years date1, date2 = date_string:match ("(%d%d%d%d?).-(%d%d%d%d?)"); if year ~= date1 and year ~= date2 then result = 0; end elseif mw.ustring.match(date_string, "%d%d%d%d[%-–]%d%d") then -- YYYY-YY date ranges local century; date1, century, date2 = mw.ustring.match(date_string, "((%d%d)%d%d)[%-–]+(%d%d)"); date2 = century..date2; -- convert YY to YYYY if year ~= date1 and year ~= date2 then result = 0; end elseif date_string:match ("%d%d%d%d?") then -- any of the standard formats of date with one year date1 = date_string:match ("(%d%d%d%d?)"); if year ~= date1 then result = 0; end else -- should never get here; this function called only when no other date errors result = 0; -- no recognizable year in date end if 0 == result then -- year / date mismatch table.insert (error_list, substitute (cfg.messages['mismatch'], {year_origin, date_origin})); -- add error message to error_list sequence table elseif 1 == result then -- redundant year / date set_message ('maint_date_year'); -- add a maint cat end end --[[--------------------------< R E F O R M A T T E R >-------------------------------------------------------- reformat 'date' into new format specified by format_param if pattern_idx (the current format of 'date') can be reformatted. Does the grunt work for reformat_dates(). The table re_formats maps pattern_idx (current format) and format_param (desired format) to a table that holds: format string used by string.format() identifier letters ('d', 'm', 'y', 'd2', 'm2', 'y2') that serve as indexes into a table t{} that holds captures from mw.ustring.match() for the various date parts specified by patterns[pattern_idx][1] Items in patterns{} have the general form: ['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'}, where: ['ymd'] is pattern_idx patterns['ymd'][1] is the match pattern with captures for mw.ustring.match() patterns['ymd'][2] is an indicator letter identifying the content of the first capture patterns['ymd'][3] ... the second capture etc. when a pattern matches a date, the captures are loaded into table t{} in capture order using the idemtifier characters as indexes into t{} For the above, a ymd date is in t{} as: t.y = first capture (year), t.m = second capture (month), t.d = third capture (day) To reformat, this function is called with the pattern_idx that matches the current format of the date and with format_param set to the desired format. This function loads table t{} as described and then calls string.format() with the format string specified by re_format[pattern_idx][format_param][1] using values taken from t{} according to the capture identifier letters specified by patterns[pattern_idx][format_param][n] where n is 2.. ]] local re_formats = { ['ymd'] = { -- date format is ymd; reformat to: ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki }, ['Mdy'] = { -- date format is Mdy; reformat to: ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- for long/short reformatting ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki }, ['dMy'] = { -- date format is dMy; reformat to: ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- for long/short reformatting ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki }, ['Md-dy'] = { -- date format is Md-dy; reformat to: ['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- for long/short reformatting ['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- |df=dmy -> d-dMy }, ['d-dMy'] = { -- date format is d-d>y; reformat to: ['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- for long/short reformatting ['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- |df=mdy -> Md-dy }, ['dM-dMy'] = { -- date format is dM-dMy; reformat to: ['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- for long/short reformatting ['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- |df=mdy -> Md-Mdy }, ['Md-Mdy'] = { -- date format is Md-Mdy; reformat to: ['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- for long/short reformatting ['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- |df=dmy -> dM-dMy }, ['dMy-dMy'] = { -- date format is dMy-dMy; reformat to: ['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- for long/short reformatting ['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- |df=mdy -> Mdy-Mdy }, ['Mdy-Mdy'] = { -- date format is Mdy-Mdy; reformat to: ['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- for long/short reformatting ['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- |df=dmy -> dMy-dMy }, ['My-My'] = { -- these for long/short reformatting ['any'] = {'%s %s – %s %s', 'm', 'y', 'm2', 'y2'}, -- dmy/mdy agnostic }, ['M-My'] = { -- these for long/short reformatting ['any'] = {'%s–%s %s', 'm', 'm2', 'y'}, -- dmy/mdy agnostic }, ['My'] = { -- these for long/short reformatting ['any'] = {'%s %s', 'm', 'y'}, -- dmy/mdy agnostic }, -- ['yMd'] = { -- not supported at en.wiki -- ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy -- ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy -- ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd -- }, } local function reformatter (date, pattern_idx, format_param, mon_len) if not in_array (pattern_idx, {'ymd', 'Mdy', 'Md-dy', 'dMy', 'yMd', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then return; -- not in this set of date format patterns then not a reformattable date end if 'ymd' == format_param and in_array (pattern_idx, {'ymd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then return; -- ymd date ranges not supported at en.wiki; no point in reformatting ymd to ymd end if in_array (pattern_idx, {'My', 'M-My', 'My-My'}) then -- these are not dmy/mdy so can't be 'reformatted' into either format_param = 'any'; -- so format-agnostic end -- yMd is not supported at en.wiki; when yMd is supported at your wiki, uncomment the next line -- if 'yMd' == format_param and in_array (pattern_idx, {'yMd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy'}) then -- these formats not convertable; yMd not supported at en.wiki if 'yMd' == format_param then -- yMd not supported at en.wiki; when yMd is supported at your wiki, remove or comment-out this line return; -- not a reformattable date end local c1, c2, c3, c4, c5, c6, c7; -- these hold the captures specified in patterns[pattern_idx][1] c1, c2, c3, c4, c5, c6, c7 = mw.ustring.match (date, patterns[pattern_idx][1]); -- get the captures local t = { -- table that holds k/v pairs of date parts from the captures and patterns[pattern_idx][2..] [patterns[pattern_idx][2]] = c1; -- at minimum there is always one capture with a matching indicator letter [patterns[pattern_idx][3] or 'x'] = c2; -- patterns can have a variable number of captures; each capture requires an indicator letter; [patterns[pattern_idx][4] or 'x'] = c3; -- where there is no capture, there is no indicator letter so n in patterns[pattern_idx][n] will be nil; [patterns[pattern_idx][5] or 'x'] = c4; -- the 'x' here spoofs an indicator letter to prevent 'table index is nil' error [patterns[pattern_idx][6] or 'x'] = c5; [patterns[pattern_idx][7] or 'x'] = c6; [patterns[pattern_idx][8] or 'x'] = c7; }; if t.a then -- if this date has an anchor year capture (all convertable date formats except ymd) if t.y2 then -- for year range date formats t.y2 = t.a; -- use the anchor year capture when reassembling the date else -- here for single date formats (except ymd) t.y = t.a; -- use the anchor year capture when reassembling the date end end if tonumber(t.m) then -- if raw month is a number (converting from ymd) if 's' == mon_len then -- if we are to use abbreviated month names t.m = cfg.date_names['inv_local_short'][tonumber(t.m)]; -- convert it to a month name else t.m = cfg.date_names['inv_local_long'][tonumber(t.m)]; -- convert it to a month name end t.d = t.d:gsub ('0(%d)', '%1'); -- strip leading '0' from day if present elseif 'ymd' == format_param then -- when converting to ymd t.y = t.y:gsub ('%a', ''); -- strip CITREF disambiguator if present; anchor year already known so process can proceed; TODO: maint message? if 1582 > tonumber (t.y) then -- ymd format dates not allowed before 1582 return; end t.m = string.format ('%02d', get_month_number (t.m)); -- make sure that month and day are two digits t.d = string.format ('%02d', t.d); elseif mon_len then -- if mon_len is set to either 'short' or 'long' for _, mon in ipairs ({'m', 'm2'}) do -- because there can be two month names, check both if t[mon] then t[mon] = get_month_number (t[mon]); -- get the month number for this month (is length agnostic) if 0 == t[mon] then return; end -- seasons and named dates can't be converted t[mon] = (('s' == mon_len) and cfg.date_names['inv_local_short'][t[mon]]) or cfg.date_names['inv_local_long'][t[mon]]; -- fetch month name according to length end end end local new_date = string.format (re_formats[pattern_idx][format_param][1], -- format string t[re_formats[pattern_idx][format_param][2]], -- named captures from t{} t[re_formats[pattern_idx][format_param][3]], t[re_formats[pattern_idx][format_param][4]], t[re_formats[pattern_idx][format_param][5]], t[re_formats[pattern_idx][format_param][6]], t[re_formats[pattern_idx][format_param][7]], t[re_formats[pattern_idx][format_param][8]] ); return new_date; end --[[-------------------------< R E F O R M A T _ D A T E S >-------------------------------------------------- Reformats existing dates into the format specified by format. format is one of several manual keywords: dmy, dmy-all, mdy, mdy-all, ymd, ymd-all. The -all version includes access- and archive-dates; otherwise these dates are not reformatted. This function allows automatic date formatting. In ~/Configuration, the article source is searched for one of the {{use xxx dates}} templates. If found, xxx becomes the global date format as xxx-all. If |cs1-dates= in {{use xxx dates}} has legitimate value then that value determines how cs1|2 dates will be rendered. Legitimate values for |cs1-dates= are: l - all dates are rendered with long month names ls - publication dates use long month names; access-/archive-dates use abbreviated month names ly - publication dates use long month names; access-/archive-dates rendered in ymd format s - all dates are rendered with abbreviated (short) month names sy - publication dates use abbreviated month names; access-/archive-dates rendered in ymd format y - all dates are rendered in ymd format the format argument for automatic date formatting will be the format specified by {{use xxx dates}} with the value supplied by |cs1-dates so one of: xxx-l, xxx-ls, xxx-ly, xxx-s, xxx-sy, xxx-y, or simply xxx (|cs1-dates= empty, omitted, or invalid) where xxx shall be either of dmy or mdy. dates are extracted from date_parameters_list, reformatted (if appropriate), and then written back into the list in the new format. Dates in date_parameters_list are presumed here to be valid (no errors). This function returns true when a date has been reformatted, false else. Actual reformatting is done by reformatter(). ]] local function reformat_dates (date_parameters_list, format) local all = false; -- set to false to skip access- and archive-dates local len_p = 'l'; -- default publication date length shall be long local len_a = 'l'; -- default access-/archive-date length shall be long local result = false; local new_date; if format:match('%a+%-all') then -- manual df keyword; auto df keyword when length not specified in {{use xxx dates}}; format = format:match('(%a+)%-all'); -- extract the format all = true; -- all dates are long format dates because this keyword doesn't specify length elseif format:match('%a+%-[lsy][sy]?') then -- auto df keywords; internal only all = true; -- auto df applies to all dates; use length specified by capture len_p for all dates format, len_p, len_a = format:match('(%a+)%-([lsy])([sy]?)'); -- extract the format and length keywords if 'y' == len_p then -- because allowed by MOS:DATEUNIFY (sort of) range dates and My dates not reformatted format = 'ymd'; -- override {{use xxx dates}} elseif (not is_set(len_a)) or (len_p == len_a) then -- no access-/archive-date length specified or same length as publication dates then len_a = len_p; -- in case len_a not set end end -- else only publication dates and they are long for param_name, param_val in pairs (date_parameters_list) do -- for each date-holding parameter in the list if is_set (param_val.val) then -- if the parameter has a value if not (not all and in_array (param_name, {'access-date', 'archive-date'})) then -- skip access- or archive-date unless format is xxx-all; yeah, ugly; TODO: find a better way for pattern_idx, pattern in pairs (patterns) do if mw.ustring.match (param_val.val, pattern[1]) then if all and in_array (param_name, {'access-date', 'archive-date'}) then -- if this date is an access- or archive-date new_date = reformatter (param_val.val, pattern_idx, (('y' == len_a) and 'ymd') or format, len_a); -- choose ymd or dmy/mdy according to len_a setting else -- all other dates new_date = reformatter (param_val.val, pattern_idx, format, len_p); end if new_date then -- set when date was reformatted date_parameters_list[param_name].val = new_date; -- update date in date list result = true; -- and announce that changes have been made end end -- if end -- for end -- if end -- if end -- for return result; -- declare boolean result and done end --[[--------------------------< D A T E _ H Y P H E N _ T O _ D A S H >---------------------------------------- Loops through the list of date-holding parameters and converts any hyphen to an ndash. Not called if the cs1|2 template has any date errors. Modifies the date_parameters_list and returns true if hyphens are replaced, else returns false. ]] local function date_hyphen_to_dash (date_parameters_list) local result = false; local n; for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list if is_set (param_val.val) and not mw.ustring.match (param_val.val, patterns.ymd[1]) then -- for those that are not ymd dates (ustring because here digits may not be Western) param_val.val, n = param_val.val:gsub ('%-', '–'); -- replace any hyphen with ndash if 0 ~= n then date_parameters_list[param_name].val = param_val.val; -- update the list result = true; end end end return result; -- so we know if any hyphens were replaced end --[[-------------------------< D A T E _ N A M E _ X L A T E >------------------------------------------------ Attempts to translate English date names to local-language date names using names supplied by MediaWiki's date parser function. This is simple name-for-name replacement and may not work for all languages. if xlat_dig is true, this function will also translate Western (English) digits to the local language's digits. This will also translate ymd dates. ]] local function date_name_xlate (date_parameters_list, xlt_dig) local xlate; local mode; -- long or short month names local modified = false; local date; local sources_t = { {cfg.date_names.en.long, cfg.date_names.inv_local_long}, -- for translating long English month names to long local month names {cfg.date_names.en.short, cfg.date_names.inv_local_short}, -- short month names {cfg.date_names.en.quarter, cfg.date_names.inv_local_quarter}, -- quarter date names {cfg.date_names.en.season, cfg.date_names.inv_local_season}, -- season date nam {cfg.date_names.en.named, cfg.date_names.inv_local_named}, -- named dates } local function is_xlateable (month) -- local function to get local date name that replaces existing English-language date name for _, date_names_t in ipairs (sources_t) do -- for each sequence table in date_names_t if date_names_t[1][month] then -- if date name is English month (long or short), quarter, season or named and if date_names_t[2][date_names_t[1][month]] then -- if there is a matching local date name return date_names_t[2][date_names_t[1][month]]; -- return the local date name end end end end for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list if is_set(param_val.val) then -- if the parameter has a value date = param_val.val; for month in mw.ustring.gmatch (date, '[%a ]+') do -- iterate through all date names in the date (single date or date range) month = mw.text.trim (month); -- this because quarterly dates contain whitespace xlate = is_xlateable (month); -- get translate <month>; returns translation or nil if xlate then date = mw.ustring.gsub (date, month, xlate); -- replace the English with the translation date_parameters_list[param_name].val = date; -- save the translated date modified = true; end end if xlt_dig then -- shall we also translate digits? date = date:gsub ('%d', cfg.date_names.xlate_digits); -- translate digits from Western to 'local digits' date_parameters_list[param_name].val = date; -- save the translated date modified = true; end end end return modified; end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local imported functions table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr, utilities_page_ptr) add_prop_cat = utilities_page_ptr.add_prop_cat ; -- import functions from selected Module:Citation/CS1/Utilities module is_set = utilities_page_ptr.is_set; in_array = utilities_page_ptr.in_array; set_message = utilities_page_ptr.set_message; substitute = utilities_page_ptr.substitute; wrap_style = utilities_page_ptr.wrap_style; cfg = cfg_table_ptr; -- import tables from selected Module:Citation/CS1/Configuration end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return { -- return exported functions dates = dates, year_date_check = year_date_check, reformat_dates = reformat_dates, date_hyphen_to_dash = date_hyphen_to_dash, date_name_xlate = date_name_xlate, set_selected_modules = set_selected_modules } 46ec997eed12f96ed5a030aee3a4264622c84955 Module:Citation/CS1/Whitelist 828 398 796 2023-01-14T14:43:42Z wikipedia>Trappist the monk 0 sync from sandbox; Scribunto text/plain --[[--------------------------< S U P P O R T E D P A R A M E T E R S >-------------------------------------- Because a steady-state signal conveys no useful information, whitelist.basic_arguments[] list items can have three values: true - these parameters are valid and supported parameters false - these parameters are deprecated but still supported tracked - these parameters are valid and supported parameters tracked in an eponymous properties category nil - these parameters are no longer supported. remove entirely ]] local basic_arguments = { ['accessdate'] = true, ['access-date'] = true, ['agency'] = true, ['archivedate'] = true, ['archive-date'] = true, ['archive-format'] = true, ['archiveurl'] = true, ['archive-url'] = true, ['article'] = true, ['article-format'] = true, ['article-number'] = true, -- {{cite journal}}, {{cite conference}}; {{citation}} when |journal= has a value ['article-url'] = true, ['article-url-access'] = true, ['arxiv'] = true, -- cite arxiv; here because allowed in cite ... as identifier ['asin'] = true, ['ASIN'] = true, ['asin-tld'] = true, ['at'] = true, ['author'] = true, ['author-first'] = true, ['author-given'] = true, ['author-last'] = true, ['author-surname'] = true, ['authorlink'] = true, ['author-link'] = true, ['author-mask'] = true, ['authors'] = true, ['bibcode'] = true, ['bibcode-access'] = true, ['biorxiv'] = true, -- cite biorxiv; here because allowed in cite ... as identifier ['chapter'] = true, ['chapter-format'] = true, ['chapter-url'] = true, ['chapter-url-access'] = true, ['citeseerx'] = true, -- cite citeseerx; here because allowed in cite ... as identifier ['collaboration'] = true, ['contribution'] = true, ['contribution-format'] = true, ['contribution-url'] = true, ['contribution-url-access'] = true, ['contributor'] = true, ['contributor-first'] = true, ['contributor-given'] = true, ['contributor-last'] = true, ['contributor-surname'] = true, ['contributor-link'] = true, ['contributor-mask'] = true, ['date'] = true, ['department'] = true, ['df'] = true, ['dictionary'] = true, ['display-authors'] = true, ['display-contributors'] = true, ['display-editors'] = true, ['display-interviewers'] = true, ['display-subjects'] = true, ['display-translators'] = true, ['doi'] = true, ['DOI'] = true, ['doi-access'] = true, ['doi-broken-date'] = true, ['edition'] = true, ['editor'] = true, ['editor-first'] = true, ['editor-given'] = true, ['editor-last'] = true, ['editor-surname'] = true, ['editor-link'] = true, ['editor-mask'] = true, ['eissn'] = true, ['EISSN'] = true, ['encyclopaedia'] = true, ['encyclopedia'] = true, ['entry'] = true, ['entry-format'] = true, ['entry-url'] = true, ['entry-url-access'] = true, ['eprint'] = true, -- cite arxiv; here because allowed in cite ... as identifier ['first'] = true, ['format'] = true, ['given'] = true, ['hdl'] = true, ['HDL'] = true, ['hdl-access'] = true, ['host'] = true, -- unique to certain templates? ['id'] = true, ['ID'] = true, ['institution'] = true, -- constrain to cite thesis? ['interviewer'] = true, ['interviewer-first'] = true, ['interviewer-given'] = true, ['interviewer-last'] = true, ['interviewer-surname'] = true, ['interviewer-link'] = true, ['interviewer-mask'] = true, ['isbn'] = true, ['ISBN'] = true, ['ismn'] = true, ['ISMN'] = true, ['issn'] = true, ['ISSN'] = true, ['issue'] = true, ['jfm'] = true, ['JFM'] = true, ['journal'] = true, ['jstor'] = true, ['JSTOR'] = true, ['jstor-access'] = true, ['lang'] = true, ['language'] = true, ['last'] = true, ['lay-date'] = false, ['lay-format'] = false, ['lay-source'] = false, ['lay-url'] = false, ['lccn'] = true, ['LCCN'] = true, ['location'] = true, ['magazine'] = true, ['medium'] = true, ['minutes'] = true, -- constrain to cite AV media and podcast? ['mode'] = true, ['mr'] = true, ['MR'] = true, ['name-list-style'] = true, ['newspaper'] = true, ['no-pp'] = true, ['no-tracking'] = true, ['number'] = true, ['oclc'] = true, ['OCLC'] = true, ['ol'] = true, ['OL'] = true, ['ol-access'] = true, ['orig-date'] = true, ['origyear'] = true, ['orig-year'] = true, ['osti'] = true, ['OSTI'] = true, ['osti-access'] = true, ['others'] = true, ['p'] = true, ['page'] = true, ['pages'] = true, ['people'] = true, ['periodical'] = true, ['place'] = true, ['pmc'] = true, ['PMC'] = true, ['pmc-embargo-date'] = true, ['pmid'] = true, ['PMID'] = true, ['postscript'] = true, ['pp'] = true, ['publication-date'] = true, ['publication-place'] = true, ['publisher'] = true, ['quotation'] = true, ['quote'] = true, ['quote-page'] = true, ['quote-pages'] = true, ['ref'] = true, ['rfc'] = true, ['RFC'] = true, ['sbn'] = true, ['SBN'] = true, ['scale'] = true, ['script-article'] = true, ['script-chapter'] = true, ['script-contribution'] = true, ['script-entry'] = true, ['script-journal'] = true, ['script-magazine'] = true, ['script-newspaper'] = true, ['script-periodical'] = true, ['script-quote'] = true, ['script-section'] = true, ['script-title'] = true, ['script-website'] = true, ['script-work'] = true, ['section'] = true, ['section-format'] = true, ['section-url'] = true, ['section-url-access'] = true, ['series'] = true, ['ssrn'] = true, -- cite ssrn; these three here because allowed in cite ... as identifier ['SSRN'] = true, ['ssrn-access'] = true, ['subject'] = true, ['subject-link'] = true, ['subject-mask'] = true, ['surname'] = true, ['s2cid'] = true, ['S2CID'] = true, ['s2cid-access'] = true, ['template-doc-demo'] = true, ['time'] = true, -- constrain to cite av media and podcast? ['time-caption'] = true, -- constrain to cite av media and podcast? ['title'] = true, ['title-link'] = true, ['translator'] = true, ['translator-first'] = true, ['translator-given'] = true, ['translator-last'] = true, ['translator-surname'] = true, ['translator-link'] = true, ['translator-mask'] = true, ['trans-article'] = true, ['trans-chapter'] = true, ['trans-contribution'] = true, ['trans-entry'] = true, ['trans-journal'] = true, ['trans-magazine'] = true, ['trans-newspaper'] = true, ['trans-periodical'] = true, ['trans-quote'] = true, ['trans-section'] = true, ['trans-title'] = true, ['trans-website'] = true, ['trans-work'] = true, ['type'] = true, ['url'] = true, ['URL'] = true, ['url-access'] = true, ['url-status'] = true, ['vauthors'] = true, ['veditors'] = true, ['version'] = true, ['via'] = true, ['volume'] = true, ['website'] = true, ['work'] = true, ['year'] = true, ['zbl'] = true, ['ZBL'] = true, } local numbered_arguments = { ['author#'] = true, ['author-first#'] = true, ['author#-first'] = true, ['author-given#'] = true, ['author#-given'] = true, ['author-last#'] = true, ['author#-last'] = true, ['author-surname#'] = true, ['author#-surname'] = true, ['author-link#'] = true, ['author#-link'] = true, ['authorlink#'] = true, ['author#link'] = true, ['author-mask#'] = true, ['author#-mask'] = true, ['contributor#'] = true, ['contributor-first#'] = true, ['contributor#-first'] = true, ['contributor-given#'] = true, ['contributor#-given'] = true, ['contributor-last#'] = true, ['contributor#-last'] = true, ['contributor-surname#'] = true, ['contributor#-surname'] = true, ['contributor-link#'] = true, ['contributor#-link'] = true, ['contributor-mask#'] = true, ['contributor#-mask'] = true, ['editor#'] = true, ['editor-first#'] = true, ['editor#-first'] = true, ['editor-given#'] = true, ['editor#-given'] = true, ['editor-last#'] = true, ['editor#-last'] = true, ['editor-surname#'] = true, ['editor#-surname'] = true, ['editor-link#'] = true, ['editor#-link'] = true, ['editor-mask#'] = true, ['editor#-mask'] = true, ['first#'] = true, ['given#'] = true, ['host#'] = true, ['interviewer#'] = true, ['interviewer-first#'] = true, ['interviewer#-first'] = true, ['interviewer-given#'] = true, ['interviewer#-given'] = true, ['interviewer-last#'] = true, ['interviewer#-last'] = true, ['interviewer-surname#'] = true, ['interviewer#-surname'] = true, ['interviewer-link#'] = true, ['interviewer#-link'] = true, ['interviewer-mask#'] = true, ['interviewer#-mask'] = true, ['last#'] = true, ['subject#'] = true, ['subject-link#'] = true, ['subject#-link'] = true, ['subject-mask#'] = true, ['subject#-mask'] = true, ['surname#'] = true, ['translator#'] = true, ['translator-first#'] = true, ['translator#-first'] = true, ['translator-given#'] = true, ['translator#-given'] = true, ['translator-last#'] = true, ['translator#-last'] = true, ['translator-surname#'] = true, ['translator#-surname'] = true, ['translator-link#'] = true, ['translator#-link'] = true, ['translator-mask#'] = true, ['translator#-mask'] = true, } --[[--------------------------< P R E P R I N T S U P P O R T E D P A R A M E T E R S >-------------------- Cite arXiv, cite biorxiv, cite citeseerx, and cite ssrn are preprint templates that use the limited set of parameters defined in the limited_basic_arguments and limited_numbered_arguments tables. Those lists are supplemented with a template-specific list of parameters that are required by the particular template and may be exclusive to one of the preprint templates. Some of these parameters may also be available to the general cs1|2 templates. Same conventions for true/false/tracked/nil as above. ]] local preprint_arguments = { arxiv = { ['arxiv'] = true, -- cite arxiv and arxiv identifiers ['class'] = true, ['eprint'] = true, -- cite arxiv and arxiv identifiers }, biorxiv = { ['biorxiv'] = true, }, citeseerx = { ['citeseerx'] = true, }, ssrn = { ['ssrn'] = true, ['SSRN'] = true, ['ssrn-access'] = true, }, } --[[--------------------------< L I M I T E D S U P P O R T E D P A R A M E T E R S >---------------------- cite arxiv, cite biorxiv, cite citeseerx, and cite ssrn templates are preprint templates so are allowed only a limited subset of parameters allowed to all other cs1|2 templates. The limited subset is defined here. Same conventions for true/false/tracked/nil as above. ]] local limited_basic_arguments = { ['at'] = true, ['author'] = true, ['author-first'] = true, ['author-given'] = true, ['author-last'] = true, ['author-surname'] = true, ['author-link'] = true, ['authorlink'] = true, ['author-mask'] = true, ['authors'] = true, ['collaboration'] = true, ['date'] = true, ['df'] = true, ['display-authors'] = true, ['first'] = true, ['given'] = true, ['language'] = true, ['last'] = true, ['mode'] = true, ['name-list-style'] = true, ['no-tracking'] = true, ['p'] = true, ['page'] = true, ['pages'] = true, ['postscript'] = true, ['pp'] = true, ['quotation'] = true, ['quote'] = true, ['ref'] = true, ['surname'] = true, ['template-doc-demo'] = true, ['title'] = true, ['trans-title'] = true, ['vauthors'] = true, ['year'] = true, } local limited_numbered_arguments = { ['author#'] = true, ['author-first#'] = true, ['author#-first'] = true, ['author-given#'] = true, ['author#-given'] = true, ['author-last#'] = true, ['author#-last'] = true, ['author-surname#'] = true, ['author#-surname'] = true, ['author-link#'] = true, ['author#-link'] = true, ['authorlink#'] = true, ['author#link'] = true, ['author-mask#'] = true, ['author#-mask'] = true, ['first#'] = true, ['given#'] = true, ['last#'] = true, ['surname#'] = true, } --[[--------------------------< U N I Q U E _ A R G U M E N T S >---------------------------------------------- Some templates have unique parameters. Those templates and their unique parameters are listed here. Keys in this table are the template's CitationClass parameter value Same conventions for true/false/tracked/nil as above. ]] local unique_arguments = { ['audio-visual'] = { ['transcript'] = true, ['transcript-format'] = true, ['transcript-url'] = true, }, conference = { ['book-title'] = true, ['conference'] = true, ['conference-format'] = true, ['conference-url'] = true, ['event'] = true, }, episode = { ['airdate'] = true, ['air-date'] = true, ['credits'] = true, ['episode-link'] = true, -- alias of |title-link= ['network'] = true, ['season'] = true, ['series-link'] = true, ['series-no'] = true, ['series-number'] = true, ['station'] = true, ['transcript'] = true, ['transcript-format'] = true, ['transcripturl'] = false, ['transcript-url'] = true, }, mailinglist = { ['mailing-list'] = true, }, map = { ['cartography'] = true, ['inset'] = true, ['map'] = true, ['map-format'] = true, ['map-url'] = true, ['map-url-access'] = true, ['script-map'] = true, ['sections'] = true, ['sheet'] = true, ['sheets'] = true, ['trans-map'] = true, }, newsgroup = { ['message-id'] = true, ['newsgroup'] = true, }, report = { ['docket'] = true, }, serial = { ['airdate'] = true, ['air-date'] = true, ['credits'] = true, ['episode'] = true, -- cite serial only TODO: make available to cite episode? ['episode-link'] = true, -- alias of |title-link= ['network'] = true, ['series-link'] = true, ['station'] = true, }, speech = { ['conference'] = true, ['conference-format'] = true, ['conference-url'] = true, ['event'] = true, }, thesis = { ['degree'] = true, ['docket'] = true, }, } --[[--------------------------< T E M P L A T E _ L I S T _ G E T >-------------------------------------------- gets a list of the templates from table t ]] local function template_list_get (t) local out = {}; -- a table for output for k, _ in pairs (t) do -- spin through the table and collect the keys table.insert (out, k) -- add each key to the output table end return out; -- and done end --[[--------------------------< E X P O R T E D T A B L E S >------------------------------------------------ ]] return { basic_arguments = basic_arguments, numbered_arguments = numbered_arguments, limited_basic_arguments = limited_basic_arguments, limited_numbered_arguments = limited_numbered_arguments, preprint_arguments = preprint_arguments, preprint_template_list = template_list_get (preprint_arguments), -- make a template list from preprint_arguments{} table unique_arguments = unique_arguments, unique_param_template_list = template_list_get (unique_arguments), -- make a template list from unique_arguments{} table }; 7c70519c4a7fa5776be7289982de9107c7a95c04 Module:Citation/CS1 828 396 792 2023-01-14T14:43:47Z wikipedia>Trappist the monk 0 sync from sandbox; Scribunto text/plain require('strict'); --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- each of these counts against the Lua upvalue limit ]] local validation; -- functions in Module:Citation/CS1/Date_validation local utilities; -- functions in Module:Citation/CS1/Utilities local z = {}; -- table of tables in Module:Citation/CS1/Utilities local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers local metadata; -- functions in Module:Citation/CS1/COinS local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist --[[------------------< P A G E S C O P E V A R I A B L E S >--------------- declare variables here that have page-wide scope that are not brought in from other modules; that are created here and used here ]] local added_deprecated_cat; -- Boolean flag so that the category is added only once local added_vanc_errs; -- Boolean flag so we only emit one Vancouver error / category local added_generic_name_errs; -- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered local Frame; -- holds the module's frame table local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module) local is_sandbox; -- true when using sandbox modules to render citation --[[--------------------------< F I R S T _ S E T >------------------------------------------------------------ Locates and returns the first set value in a table of values where the order established in the table, left-to-right (or top-to-bottom), is the order in which the values are evaluated. Returns nil if none are set. This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs. With the pairs version the order of evaluation could not be guaranteed. With the ipairs version, a nil value would terminate the for-loop before it reached the actual end of the list. ]] local function first_set (list, count) local i = 1; while i <= count do -- loop through all items in list if utilities.is_set( list[i] ) then return list[i]; -- return the first set list member end i = i + 1; -- point to next end end --[[--------------------------< A D D _ V A N C _ E R R O R >---------------------------------------------------- Adds a single Vancouver system error message to the template's output regardless of how many error actually exist. To prevent duplication, added_vanc_errs is nil until an error message is emitted. added_vanc_errs is a Boolean declared in page scope variables above ]] local function add_vanc_error (source, position) if added_vanc_errs then return end added_vanc_errs = true; -- note that we've added this category utilities.set_message ('err_vancouver', {source, position}); end --[[--------------------------< I S _ S C H E M E >------------------------------------------------------------ does this thing that purports to be a URI scheme seem to be a valid scheme? The scheme is checked to see if it is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says: Scheme names consist of a sequence of characters beginning with a letter and followed by any combination of letters, digits, plus ("+"), period ("."), or hyphen ("-"). returns true if it does, else false ]] local function is_scheme (scheme) return scheme and scheme:match ('^%a[%a%d%+%.%-]*:'); -- true if scheme is set and matches the pattern end --[=[-------------------------< I S _ D O M A I N _ N A M E >-------------------------------------------------- Does this thing that purports to be a domain name seem to be a valid domain name? Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5 BNF defined here: https://tools.ietf.org/html/rfc4234 Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15; see also [[Single-letter second-level domain]] list of TLDs: https://www.iana.org/domains/root/db RFC 952 (modified by RFC 1123) requires the first and last character of a hostname to be a letter or a digit. Between the first and last characters the name may use letters, digits, and the hyphen. Also allowed are IPv4 addresses. IPv6 not supported domain is expected to be stripped of any path so that the last character in the last character of the TLD. tld is two or more alpha characters. Any preceding '//' (from splitting a URL with a scheme) will be stripped here. Perhaps not necessary but retained in case it is necessary for IPv4 dot decimal. There are several tests: the first character of the whole domain name including subdomains must be a letter or a digit internationalized domain name (ASCII characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the TLD) see https://tools.ietf.org/html/rfc3490 single-letter/digit second-level domains in the .org, .cash, and .today TLDs q, x, and z SL domains in the .com TLD i and q SL domains in the .net TLD single-letter SL domains in the ccTLDs (where the ccTLD is two letters) two-character SL domains in gTLDs (where the gTLD is two or more letters) three-plus-character SL domains in gTLDs (where the gTLD is two or more letters) IPv4 dot-decimal address format; TLD not allowed returns true if domain appears to be a proper name and TLD or IPv4 address, else false ]=] local function is_domain_name (domain) if not domain then return false; -- if not set, abandon end domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once if not domain:match ('^[%w]') then -- first character must be letter or digit return false; end if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at Wikisource return false; end local patterns = { -- patterns that look like URLs '%f[%w][%w][%w%-]+[%w]%.%a%a+$', -- three or more character hostname.hostname or hostname.tld '%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$', -- internationalized domain name with ACE prefix '%f[%a][qxz]%.com$', -- assigned one character .com hostname (x.com times out 2015-12-10) '%f[%a][iq]%.net$', -- assigned one character .net hostname (q.net registered but not active 2015-12-10) '%f[%w][%w]%.%a%a$', -- one character hostname and ccTLD (2 chars) '%f[%w][%w][%w]%.%a%a+$', -- two character hostname and TLD '^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?', -- IPv4 address } for _, pattern in ipairs (patterns) do -- loop through the patterns list if domain:match (pattern) then return true; -- if a match then we think that this thing that purports to be a URL is a URL end end for _, d in ipairs (cfg.single_letter_2nd_lvl_domains_t) do -- look for single letter second level domain names for these top level domains if domain:match ('%f[%w][%w]%.' .. d) then return true end end return false; -- no matches, we don't know what this thing is end --[[--------------------------< I S _ U R L >------------------------------------------------------------------ returns true if the scheme and domain parts of a URL appear to be a valid URL; else false. This function is the last step in the validation process. This function is separate because there are cases that are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external wikilinks. ]] local function is_url (scheme, domain) if utilities.is_set (scheme) then -- if scheme is set check it and domain return is_scheme (scheme) and is_domain_name (domain); else return is_domain_name (domain); -- scheme not set when URL is protocol-relative end end --[[--------------------------< S P L I T _ U R L >------------------------------------------------------------ Split a URL into a scheme, authority indicator, and domain. First remove Fully Qualified Domain Name terminator (a dot following TLD) (if any) and any path(/), query(?) or fragment(#). If protocol-relative URL, return nil scheme and domain else return nil for both scheme and domain. When not protocol-relative, get scheme, authority indicator, and domain. If there is an authority indicator (one or more '/' characters immediately following the scheme's colon), make sure that there are only 2. Any URL that does not have news: scheme must have authority indicator (//). TODO: are there other common schemes like news: that don't use authority indicator? Strip off any port and path; ]] local function split_url (url_str) local scheme, authority, domain; url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1'); -- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//') if url_str:match ('^//%S*') then -- if there is what appears to be a protocol-relative URL domain = url_str:match ('^//(%S*)') elseif url_str:match ('%S-:/*%S+') then -- if there is what appears to be a scheme, optional authority indicator, and domain name scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)'); -- extract the scheme, authority indicator, and domain portions if utilities.is_set (authority) then authority = authority:gsub ('//', '', 1); -- replace place 1 pair of '/' with nothing; if utilities.is_set(authority) then -- if anything left (1 or 3+ '/' where authority should be) then return scheme; -- return scheme only making domain nil which will cause an error message end else if not scheme:match ('^news:') then -- except for news:..., MediaWiki won't link URLs that do not have authority indicator; TODO: a better way to do this test? return scheme; -- return scheme only making domain nil which will cause an error message end end domain = domain:gsub ('(%a):%d+', '%1'); -- strip port number if present end return scheme, domain; end --[[--------------------------< L I N K _ P A R A M _ O K >--------------------------------------------------- checks the content of |title-link=, |series-link=, |author-link=, etc. for properly formatted content: no wikilinks, no URLs Link parameters are to hold the title of a Wikipedia article, so none of the WP:TITLESPECIALCHARACTERS are allowed: # < > [ ] | { } _ except the underscore which is used as a space in wiki URLs and # which is used for section links returns false when the value contains any of these characters. When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid URL (the |<param>-link= parameter is ok); else false when value appears to be a valid URL (the |<param>-link= parameter is NOT ok). ]] local function link_param_ok (value) local scheme, domain; if value:find ('[<>%[%]|{}]') then -- if any prohibited characters return false; end scheme, domain = split_url (value); -- get scheme or nil and domain or nil from URL; return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid URL end --[[--------------------------< L I N K _ T I T L E _ O K >--------------------------------------------------- Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value. |<title>= may be wiki-linked but not when |<param>-link= has a value. This function emits an error message when that condition exists check <link> for inter-language interwiki-link prefix. prefix must be a MediaWiki-recognized language code and must begin with a colon. ]] local function link_title_ok (link, lorig, title, torig) local orig; if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value if not link_param_ok (link) then -- check |<param>-link= markup orig = lorig; -- identify the failing link parameter elseif title:find ('%[%[') then -- check |title= for wikilink markup orig = torig; -- identify the failing |title= parameter elseif link:match ('^%a+:') then -- if the link is what looks like an interwiki local prefix = link:match ('^(%a+):'):lower(); -- get the interwiki prefix if cfg.inter_wiki_map[prefix] then -- if prefix is in the map, must have preceding colon orig = lorig; -- flag as error end end end if utilities.is_set (orig) then link = ''; -- unset utilities.set_message ('err_bad_paramlink', orig); -- URL or wikilink in |title= with |title-link=; end return link; -- link if ok, empty string else end --[[--------------------------< C H E C K _ U R L >------------------------------------------------------------ Determines whether a URL string appears to be valid. First we test for space characters. If any are found, return false. Then split the URL into scheme and domain portions, or for protocol-relative (//example.com) URLs, just the domain. Use is_url() to validate the two portions of the URL. If both are valid, or for protocol-relative if domain is valid, return true, else false. Because it is different from a standard URL, and because this module used external_link() to make external links that work for standard and news: links, we validate newsgroup names here. The specification for a newsgroup name is at https://tools.ietf.org/html/rfc5536#section-3.1.4 ]] local function check_url( url_str ) if nil == url_str:match ("^%S+$") then -- if there are any spaces in |url=value it can't be a proper URL return false; end local scheme, domain; scheme, domain = split_url (url_str); -- get scheme or nil and domain or nil from URL; if 'news:' == scheme then -- special case for newsgroups return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$'); end return is_url (scheme, domain); -- return true if value appears to be a valid URL end --[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >---------------------------- Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first non-space characters following the opening bracket appear to be a URL. The test will also find external wikilinks that use protocol-relative URLs. Also finds bare URLs. The frontier pattern prevents a match on interwiki-links which are similar to scheme:path URLs. The tests that find bracketed URLs are required because the parameters that call this test (currently |title=, |chapter=, |work=, and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]] is possible as might be [[en://Hus]]. ]=] local function is_parameter_ext_wikilink (value) local scheme, domain; if value:match ('%f[%[]%[%a%S*:%S+.*%]') then -- if ext. wikilink with scheme and domain: [xxxx://yyyyy.zzz] scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]')); elseif value:match ('%f[%[]%[//%S+.*%]') then -- if protocol-relative ext. wikilink: [//yyyyy.zzz] scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]')); elseif value:match ('%a%S*:%S+') then -- if bare URL with scheme; may have leading or trailing plain text scheme, domain = split_url (value:match ('(%a%S*:%S+)')); elseif value:match ('//%S+') then -- if protocol-relative bare URL: //yyyyy.zzz; may have leading or trailing plain text scheme, domain = split_url (value:match ('(//%S+)')); -- what is left should be the domain else return false; -- didn't find anything that is obviously a URL end return is_url (scheme, domain); -- return true if value appears to be a valid URL end --[[-------------------------< C H E C K _ F O R _ U R L >----------------------------------------------------- loop through a list of parameters and their values. Look at the value and if it has an external link, emit an error message. ]] local function check_for_url (parameter_list, error_list) for k, v in pairs (parameter_list) do -- for each parameter in the list if is_parameter_ext_wikilink (v) then -- look at the value; if there is a URL add an error message table.insert (error_list, utilities.wrap_style ('parameter', k)); end end end --[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------ Escape sequences for content that will be used for URL descriptions ]] local function safe_for_url( str ) if str:match( "%[%[.-%]%]" ) ~= nil then utilities.set_message ('err_wikilink_in_url', {}); end return str:gsub( '[%[%]\n]', { ['['] = '&#91;', [']'] = '&#93;', ['\n'] = ' ' } ); end --[[--------------------------< E X T E R N A L _ L I N K >---------------------------------------------------- Format an external link with error checking ]] local function external_link (URL, label, source, access) local err_msg = ''; local domain; local path; local base_url; if not utilities.is_set (label) then label = URL; if utilities.is_set (source) then utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)}); else error (cfg.messages["bare_url_no_origin"]); -- programmer error; valid parameter name does not have matching meta-parameter end end if not check_url (URL) then utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); end domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$'); -- split the URL into scheme plus domain and path if path then -- if there is a path portion path = path:gsub ('[%[%]]', {['['] = '%5b', [']'] = '%5d'}); -- replace '[' and ']' with their percent-encoded values URL = table.concat ({domain, path}); -- and reassemble end base_url = table.concat ({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wiki-markup URL if utilities.is_set (access) then -- access level (subscription, registration, limited) base_url = utilities.substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon end return base_url; end --[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >-------------------------------------- Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated parameters in the citation. added_deprecated_cat is a Boolean declared in page scope variables above ]] local function deprecated_parameter(name) if not added_deprecated_cat then added_deprecated_cat = true; -- note that we've added this category utilities.set_message ('err_deprecated_params', {name}); -- add error message end end --[=[-------------------------< K E R N _ Q U O T E S >-------------------------------------------------------- Apply kerning to open the space between the quote mark provided by the module and a leading or trailing quote mark contained in a |title= or |chapter= parameter's value. This function will positive kern either single or double quotes: "'Unkerned title with leading and trailing single quote marks'" " 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example) Double single quotes (italic or bold wiki-markup) are not kerned. Replaces Unicode quote marks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter quote marks regardless of the need for kerning. Unicode quote marks are not replaced in simple [[D]] wikilinks. Call this function for chapter titles, for website titles, etc.; not for book titles. ]=] local function kern_quotes (str) local cap = ''; local wl_type, label, link; wl_type, label, link = utilities.is_wikilink (str); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]] if 1 == wl_type then -- [[D]] simple wikilink with or without quote marks if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then -- leading and trailing quote marks str = utilities.substitute (cfg.presentation['kern-left'], str); str = utilities.substitute (cfg.presentation['kern-right'], str); elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]') then -- leading quote marks str = utilities.substitute (cfg.presentation['kern-left'], str); elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then -- trailing quote marks str = utilities.substitute (cfg.presentation['kern-right'], str); end else -- plain text or [[L|D]]; text in label variable label = mw.ustring.gsub (label, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark) label = mw.ustring.gsub (label, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark) cap = mw.ustring.match (label, "^([\"\'][^\'].+)"); -- match leading double or single quote but not doubled single quotes (italic markup) if utilities.is_set (cap) then label = utilities.substitute (cfg.presentation['kern-left'], cap); end cap = mw.ustring.match (label, "^(.+[^\'][\"\'])$") -- match trailing double or single quote but not doubled single quotes (italic markup) if utilities.is_set (cap) then label = utilities.substitute (cfg.presentation['kern-right'], cap); end if 2 == wl_type then str = utilities.make_wikilink (link, label); -- reassemble the wikilink else str = label; end end return str; end --[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >---------------------------------------- |script-title= holds title parameters that are not written in Latin-based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should not be italicized and may be written right-to-left. The value supplied by |script-title= is concatenated onto Title after Title has been wrapped in italic markup. Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate RTL languages from the English left to right. |script-title= provides a unique feature. The value in |script-title= may be prefixed with a two-character ISO 639-1 language code and a colon: |script-title=ja:*** *** (where * represents a Japanese character) Spaces between the two-character code and the colon and the colon and the first script character are allowed: |script-title=ja : *** *** |script-title=ja: *** *** |script-title=ja :*** *** Spaces preceding the prefix are allowed: |script-title = ja:*** *** The prefix is checked for validity. If it is a valid ISO 639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can know the language the tag contains. This may help the browser render the script more correctly. If the prefix is invalid, the lang attribute is not added. At this time there is no error message for this condition. Supports |script-title=, |script-chapter=, |script-<periodical>= ]] local function format_script_value (script_value, script_param) local lang=''; -- initialize to empty string local name; if script_value:match('^%l%l%l?%s*:') then -- if first 3 or 4 non-space characters are script language prefix lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script if not utilities.is_set (lang) then utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing title part']}); -- prefix without 'title'; add error message return ''; -- script_value was just the prefix so return empty string end -- if we get this far we have prefix and script name = cfg.lang_code_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code? script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script -- is prefix one of these language codes? if utilities.in_array (lang, cfg.script_lang_codes) then utilities.add_prop_cat ('script', {name, lang}) else utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['unknown language code']}); -- unknown script-language; add error message end lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute else utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['invalid language code']}); -- invalid language code; add error message lang = ''; -- invalid so set lang to empty string end else utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing prefix']}); -- no language code prefix; add error message end script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL return script_value; end --[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------ Initially for |title= and |script-title=, this function concatenates those two parameter values after the script value has been wrapped in <bdi> tags. ]] local function script_concatenate (title, script, script_param) if utilities.is_set (script) then script = format_script_value (script, script_param); -- <bdi> tags, lang attribute, categorization, etc.; returns empty string on error if utilities.is_set (script) then title = title .. ' ' .. script; -- concatenate title and script title end end return title; end --[[--------------------------< W R A P _ M S G >-------------------------------------------------------------- Applies additional message text to various parameter values. Supplied string is wrapped using a message_list configuration taking one argument. Supports lower case text for {{citation}} templates. Additional text taken from citation_config.messages - the reason this function is similar to but separate from wrap_style(). ]] local function wrap_msg (key, str, lower) if not utilities.is_set ( str ) then return ""; end if true == lower then local msg; msg = cfg.messages[key]:lower(); -- set the message to lower case before return utilities.substitute ( msg, str ); -- including template text else return utilities.substitute ( cfg.messages[key], str ); end end --[[----------------< W I K I S O U R C E _ U R L _ M A K E >------------------- Makes a Wikisource URL from Wikisource interwiki-link. Returns the URL and appropriate label; nil else. str is the value assigned to |chapter= (or aliases) or |title= or |title-link= ]] local function wikisource_url_make (str) local wl_type, D, L; local ws_url, ws_label; local wikisource_prefix = table.concat ({'https://', cfg.this_wiki_code, '.wikisource.org/wiki/'}); wl_type, D, L = utilities.is_wikilink (str); -- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink) if 0 == wl_type then -- not a wikilink; might be from |title-link= str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace if utilities.is_set (str) then ws_url = table.concat ({ -- build a Wikisource URL wikisource_prefix, -- prefix str, -- article title }); ws_label = str; -- label for the URL end elseif 1 == wl_type then -- simple wikilink: [[Wikisource:ws article]] str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace if utilities.is_set (str) then ws_url = table.concat ({ -- build a Wikisource URL wikisource_prefix, -- prefix str, -- article title }); ws_label = str; -- label for the URL end elseif 2 == wl_type then -- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]]) str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace if utilities.is_set (str) then ws_label = D; -- get ws article name from display portion of interwiki link ws_url = table.concat ({ -- build a Wikisource URL wikisource_prefix, -- prefix str, -- article title without namespace from link portion of wikilink }); end end if ws_url then ws_url = mw.uri.encode (ws_url, 'WIKI'); -- make a usable URL ws_url = ws_url:gsub ('%%23', '#'); -- undo percent-encoding of fragment marker end return ws_url, ws_label, L or D; -- return proper URL or nil and a label or nil end --[[----------------< F O R M A T _ P E R I O D I C A L >----------------------- Format the three periodical parameters: |script-<periodical>=, |<periodical>=, and |trans-<periodical>= into a single Periodical meta-parameter. ]] local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical) if not utilities.is_set (periodical) then periodical = ''; -- to be safe for concatenation else periodical = utilities.wrap_style ('italic-title', periodical); -- style end periodical = script_concatenate (periodical, script_periodical, script_periodical_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped if utilities.is_set (trans_periodical) then trans_periodical = utilities.wrap_style ('trans-italic-title', trans_periodical); if utilities.is_set (periodical) then periodical = periodical .. ' ' .. trans_periodical; else -- here when trans-periodical without periodical or script-periodical periodical = trans_periodical; utilities.set_message ('err_trans_missing_title', {'periodical'}); end end return periodical; end --[[------------------< F O R M A T _ C H A P T E R _ T I T L E >--------------- Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=, and |chapter-url= into a single chapter meta- parameter (chapter_url_source used for error messages). ]] local function format_chapter_title (script_chapter, script_chapter_source, chapter, chapter_source, trans_chapter, trans_chapter_source, chapter_url, chapter_url_source, no_quotes, access) local ws_url, ws_label, L = wikisource_url_make (chapter); -- make a wikisource URL and label from a wikisource interwiki link if ws_url then ws_label = ws_label:gsub ('_', ' '); -- replace underscore separators with space characters chapter = ws_label; end if not utilities.is_set (chapter) then chapter = ''; -- to be safe for concatenation else if false == no_quotes then chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from module provided quote marks chapter = utilities.wrap_style ('quoted-title', chapter); end end chapter = script_concatenate (chapter, script_chapter, script_chapter_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped if utilities.is_set (chapter_url) then chapter = external_link (chapter_url, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate elseif ws_url then chapter = external_link (ws_url, chapter .. '&nbsp;', 'ws link in chapter'); -- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this? chapter = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter}); end if utilities.is_set (trans_chapter) then trans_chapter = utilities.wrap_style ('trans-quoted-title', trans_chapter); if utilities.is_set (chapter) then chapter = chapter .. ' ' .. trans_chapter; else -- here when trans_chapter without chapter or script-chapter chapter = trans_chapter; chapter_source = trans_chapter_source:match ('trans%-?(.+)'); -- when no chapter, get matching name from trans-<param> utilities.set_message ('err_trans_missing_title', {chapter_source}); end end return chapter; end --[[----------------< H A S _ I N V I S I B L E _ C H A R S >------------------- This function searches a parameter's value for non-printable or invisible characters. The search stops at the first match. This function will detect the visible replacement character when it is part of the Wikisource. Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers (gallery, math, pre, ref) and identifies them with a slightly different error message. See also coins_cleanup(). Output of this function is an error message that identifies the character or the Unicode group, or the stripmarker that was detected along with its position (or, for multi-byte characters, the position of its first byte) in the parameter value. ]] local function has_invisible_chars (param, v) local position = ''; -- position of invisible char or starting position of stripmarker local capture; -- used by stripmarker detection to hold name of the stripmarker local stripmarker; -- boolean set true when a stripmarker is found capture = string.match (v, '[%w%p ]*'); -- test for values that are simple ASCII text and bypass other tests if true if capture == v then -- if same there are no Unicode characters return; end for _, invisible_char in ipairs (cfg.invisible_chars) do local char_name = invisible_char[1]; -- the character or group name local pattern = invisible_char[2]; -- the pattern used to find it position, _, capture = mw.ustring.find (v, pattern); -- see if the parameter value contains characters that match the pattern if position and (cfg.invisible_defs.zwj == capture) then -- if we found a zero-width joiner character if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts position = nil; -- unset position elseif cfg.emoji_t[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}? position = nil; -- unset position end end if position then if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition) ('templatestyles' == capture and utilities.in_array (param, {'id', 'quote'})) then -- templatestyles stripmarker allowed in these parameters stripmarker = true; -- set a flag elseif true == stripmarker and cfg.invisible_defs.del == capture then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker position = nil; -- unset else local err_msg; if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then err_msg = capture .. ' ' .. char_name; else err_msg = char_name .. ' ' .. 'character'; end utilities.set_message ('err_invisible_char', {err_msg, utilities.wrap_style ('parameter', param), position}); -- add error message return; -- and done with this parameter end end end end --[[-------------------< A R G U M E N T _ W R A P P E R >---------------------- Argument wrapper. This function provides support for argument mapping defined in the configuration file so that multiple names can be transparently aliased to single internal variable. ]] local function argument_wrapper ( args ) local origin = {}; return setmetatable({ ORIGIN = function ( self, k ) local dummy = self[k]; -- force the variable to be loaded. return origin[k]; end }, { __index = function ( tbl, k ) if origin[k] ~= nil then return nil; end local args, list, v = args, cfg.aliases[k]; if type( list ) == 'table' then v, origin[k] = utilities.select_one ( args, list, 'err_redundant_parameters' ); if origin[k] == nil then origin[k] = ''; -- Empty string, not nil end elseif list ~= nil then v, origin[k] = args[list], list; else -- maybe let through instead of raising an error? -- v, origin[k] = args[k], k; error( cfg.messages['unknown_argument_map'] .. ': ' .. k); end -- Empty strings, not nil; if v == nil then v = ''; origin[k] = ''; end tbl = rawset( tbl, k, v ); return v; end, }); end --[[--------------------------< N O W R A P _ D A T E >------------------------- When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>. When date is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span: <span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY DOES NOT yet support MMMM YYYY or any of the date ranges. ]] local function nowrap_date (date) local cap = ''; local cap2 = ''; if date:match("^%d%d%d%d%-%d%d%-%d%d$") then date = utilities.substitute (cfg.presentation['nowrap1'], date); elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$"); date = utilities.substitute (cfg.presentation['nowrap2'], {cap, cap2}); end return date; end --[[--------------------------< S E T _ T I T L E T Y P E >--------------------- This function sets default title types (equivalent to the citation including |type=<default value>) for those templates that have defaults. Also handles the special case where it is desirable to omit the title type from the rendered citation (|type=none). ]] local function set_titletype (cite_class, title_type) if utilities.is_set (title_type) then if 'none' == cfg.keywords_xlate[title_type] then title_type = ''; -- if |type=none then type parameter not displayed end return title_type; -- if |type= has been set to any other value use that value end return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation end --[[--------------------------< S A F E _ J O I N >----------------------------- Joins a sequence of strings together while checking for duplicate separation characters. ]] local function safe_join( tbl, duplicate_char ) local f = {}; -- create a function table appropriate to type of 'duplicate character' if 1 == #duplicate_char then -- for single byte ASCII characters use the string library functions f.gsub = string.gsub f.match = string.match f.sub = string.sub else -- for multi-byte characters use the ustring library functions f.gsub = mw.ustring.gsub f.match = mw.ustring.match f.sub = mw.ustring.sub end local str = ''; -- the output string local comp = ''; -- what does 'comp' mean? local end_chr = ''; local trim; for _, value in ipairs( tbl ) do if value == nil then value = ''; end if str == '' then -- if output string is empty str = value; -- assign value to it (first time through the loop) elseif value ~= '' then if value:sub(1, 1) == '<' then -- special case of values enclosed in spans and other markup. comp = value:gsub( "%b<>", "" ); -- remove HTML markup (<span>string</span> -> string) else comp = value; end -- typically duplicate_char is sepc if f.sub(comp, 1, 1) == duplicate_char then -- is first character same as duplicate_char? why test first character? -- Because individual string segments often (always?) begin with terminal punct for the -- preceding segment: 'First element' .. 'sepc next element' .. etc.? trim = false; end_chr = f.sub(str, -1, -1); -- get the last character of the output string -- str = str .. "<HERE(enchr=" .. end_chr .. ")" -- debug stuff? if end_chr == duplicate_char then -- if same as separator str = f.sub(str, 1, -2); -- remove it elseif end_chr == "'" then -- if it might be wiki-markup if f.sub(str, -3, -1) == duplicate_char .. "''" then -- if last three chars of str are sepc'' str = f.sub(str, 1, -4) .. "''"; -- remove them and add back '' elseif f.sub(str, -5, -1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]'' trim = true; -- why? why do this and next differently from previous? elseif f.sub(str, -4, -1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]'' trim = true; -- same question end elseif end_chr == "]" then -- if it might be wiki-markup if f.sub(str, -3, -1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink trim = true; elseif f.sub(str, -3, -1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link trim = true; elseif f.sub(str, -2, -1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link trim = true; elseif f.sub(str, -4, -1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title. trim = true; end elseif end_chr == " " then -- if last char of output string is a space if f.sub(str, -2, -1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space> str = f.sub(str, 1, -3); -- remove them both end end if trim then if value ~= comp then -- value does not equal comp when value contains HTML markup local dup2 = duplicate_char; if f.match(dup2, "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows HTML markup else value = f.sub(value, 2, -1 ); -- remove duplicate_char when it is first character end end end str = str .. value; -- add it to the output string end end return str; end --[[--------------------------< I S _ S U F F I X >----------------------------- returns true if suffix is properly formed Jr, Sr, or ordinal in the range 1–9. Puncutation not allowed. ]] local function is_suffix (suffix) if utilities.in_array (suffix, {'Jr', 'Sr', 'Jnr', 'Snr', '1st', '2nd', '3rd'}) or suffix:match ('^%dth$') then return true; end return false; end --[[--------------------< I S _ G O O D _ V A N C _ N A M E >------------------- For Vancouver style, author/editor names are supposed to be rendered in Latin (read ASCII) characters. When a name uses characters that contain diacritical marks, those characters are to be converted to the corresponding Latin character. When a name is written using a non-Latin alphabet or logogram, that name is to be transliterated into Latin characters. The module doesn't do this so editors may/must. This test allows |first= and |last= names to contain any of the letters defined in the four Unicode Latin character sets [http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A [http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF [http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F [http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F |lastn= also allowed to contain hyphens, spaces, and apostrophes. (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) |firstn= also allowed to contain hyphens, spaces, apostrophes, and periods This original test: if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]+[2-6%a]*$") then was written outside of the code editor and pasted here because the code editor gets confused between character insertion point and cursor position. The test has been rewritten to use decimal character escape sequence for the individual bytes of the Unicode characters so that it is not necessary to use an external editor to maintain this code. \195\128-\195\150 – À-Ö (U+00C0–U+00D6 – C0 controls) \195\152-\195\182 – Ø-ö (U+00D8-U+00F6 – C0 controls) \195\184-\198\191 – ø-ƿ (U+00F8-U+01BF – C0 controls, Latin extended A & B) \199\132-\201\143 – DŽ-ɏ (U+01C4-U+024F – Latin extended B) ]] local function is_good_vanc_name (last, first, suffix, position) if not suffix then if first:find ('[,%s]') then -- when there is a space or comma, might be first name/initials + generational suffix first = first:match ('(.-)[,%s]+'); -- get name/initials suffix = first:match ('[,%s]+(.+)$'); -- get generational suffix end end if utilities.is_set (suffix) then if not is_suffix (suffix) then add_vanc_error (cfg.err_msg_supl.suffix, position); return false; -- not a name with an appropriate suffix end end if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%'%.]*$") then add_vanc_error (cfg.err_msg_supl['non-Latin char'], position); return false; -- not a string of Latin characters; Vancouver requires Romanization end; return true; end --[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------ Attempts to convert names to initials in support of |name-list-style=vanc. Names in |firstn= may be separated by spaces or hyphens, or for initials, a period. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/. Vancouver style requires family rank designations (Jr, II, III, etc.) to be rendered as Jr, 2nd, 3rd, etc. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/. This code only accepts and understands generational suffix in the Vancouver format because Roman numerals look like, and can be mistaken for, initials. This function uses ustring functions because firstname initials may be any of the Unicode Latin characters accepted by is_good_vanc_name (). ]] local function reduce_to_initials(first, position) local name, suffix = mw.ustring.match(first, "^(%u+) ([%dJS][%drndth]+)$"); if not name then -- if not initials and a suffix name = mw.ustring.match(first, "^(%u+)$"); -- is it just initials? end if name then -- if first is initials with or without suffix if 3 > mw.ustring.len (name) then -- if one or two initials if suffix then -- if there is a suffix if is_suffix (suffix) then -- is it legitimate? return first; -- one or two initials and a valid suffix so nothing to do else add_vanc_error (cfg.err_msg_supl.suffix, position); -- one or two initials with invalid suffix so error message return first; -- and return first unmolested end else return first; -- one or two initials without suffix; nothing to do end end end -- if here then name has 3 or more uppercase letters so treat them as a word local initials, names = {}, {}; -- tables to hold name parts and initials local i = 1; -- counter for number of initials names = mw.text.split (first, '[%s,]+'); -- split into a table of names and possible suffix while names[i] do -- loop through the table if 1 < i and names[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot) names[i] = names[i]:gsub ('%.', ''); -- remove terminal dot if present if is_suffix (names[i]) then -- if a legitimate suffix table.insert (initials, ' ' .. names[i]); -- add a separator space, insert at end of initials table break; -- and done because suffix must fall at the end of a name end -- no error message if not a suffix; possibly because of Romanization end if 3 > i then table.insert (initials, mw.ustring.sub(names[i], 1, 1)); -- insert the initial at end of initials table end i = i + 1; -- bump the counter end return table.concat(initials) -- Vancouver format does not include spaces. end --[[--------------------------< I N T E R W I K I _ P R E F I X E N _ G E T >---------------------------------- extract interwiki prefixen from <value>. Returns two one or two values: false – no prefixen nil – prefix exists but not recognized project prefix, language prefix – when value has either of: :<project>:<language>:<article> :<language>:<project>:<article> project prefix, nil – when <value> has only a known single-letter prefix nil, language prefix – when <value> has only a known language prefix accepts single-letter project prefixen: 'd' (wikidata), 's' (wikisource), and 'w' (wikipedia) prefixes; at this writing, the other single-letter prefixen (b (wikibook), c (commons), m (meta), n (wikinews), q (wikiquote), and v (wikiversity)) are not supported. ]] local function interwiki_prefixen_get (value, is_link) if not value:find (':%l+:') then -- if no prefix return false; -- abandon; boolean here to distinguish from nil fail returns later end local prefix_patterns_linked_t = { -- sequence of valid interwiki and inter project prefixen '^%[%[:([dsw]):(%l%l+):', -- wikilinked; project and language prefixes '^%[%[:(%l%l+):([dsw]):', -- wikilinked; language and project prefixes '^%[%[:([dsw]):', -- wikilinked; project prefix '^%[%[:(%l%l+):', -- wikilinked; language prefix } local prefix_patterns_unlinked_t = { -- sequence of valid interwiki and inter project prefixen '^:([dsw]):(%l%l+):', -- project and language prefixes '^:(%l%l+):([dsw]):', -- language and project prefixes '^:([dsw]):', -- project prefix '^:(%l%l+):', -- language prefix } local cap1, cap2; for _, pattern in ipairs ((is_link and prefix_patterns_linked_t) or prefix_patterns_unlinked_t) do cap1, cap2 = value:match (pattern); if cap1 then break; -- found a match so stop looking end end if cap1 and cap2 then -- when both then :project:language: or :language:project: (both forms allowed) if 1 == #cap1 then -- length == 1 then :project:language: if cfg.inter_wiki_map[cap2] then -- is language prefix in the interwiki map? return cap1, cap2; -- return interwiki project and interwiki language end else -- here when :language:project: if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map? return cap2, cap1; -- return interwiki project and interwiki language end end return nil; -- unknown interwiki language elseif not (cap1 or cap2) then -- both are nil? return nil; -- we got something that looks like a project prefix but isn't; return fail elseif 1 == #cap1 then -- here when one capture return cap1, nil; -- length is 1 so return project, nil language else -- here when one capture and its length it more than 1 if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map? return nil, cap1; -- return nil project, language end end end --[[--------------------------< L I S T _ P E O P L E >-------------------------- Formats a list of people (authors, contributors, editors, interviewers, translators) names in the list will be linked when |<name>-link= has a value |<name>-mask- does NOT have a value; masked names are presumed to have been rendered previously so should have been linked there when |<name>-mask=0, the associated name is not rendered ]] local function list_people (control, people, etal) local sep; local namesep; local format = control.format; local maximum = control.maximum; local name_list = {}; if 'vanc' == format then -- Vancouver-like name styling? sep = cfg.presentation['sep_nl_vanc']; -- name-list separator between names is a comma namesep = cfg.presentation['sep_name_vanc']; -- last/first separator is a space else sep = cfg.presentation['sep_nl']; -- name-list separator between names is a semicolon namesep = cfg.presentation['sep_name']; -- last/first separator is <comma><space> end if sep:sub (-1, -1) ~= " " then sep = sep .. " " end if utilities.is_set (maximum) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for other names for i, person in ipairs (people) do if utilities.is_set (person.last) then local mask = person.mask; local one; local sep_one = sep; if utilities.is_set (maximum) and i > maximum then etal = true; break; end if mask then local n = tonumber (mask); -- convert to a number if it can be converted; nil else if n then one = 0 ~= n and string.rep("&mdash;", n) or nil; -- make a string of (n > 0) mdashes, nil else, to replace name person.link = nil; -- don't create link to name if name is replaces with mdash string or has been set nil else one = mask; -- replace name with mask text (must include name-list separator) sep_one = " "; -- modify name-list separator end else one = person.last; -- get surname local first = person.first -- get given name if utilities.is_set (first) then if ("vanc" == format) then -- if Vancouver format one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) if not person.corporate and is_good_vanc_name (one, first, nil, i) then -- and name is all Latin characters; corporate authors not tested first = reduce_to_initials (first, i); -- attempt to convert first name(s) to initials end end one = one .. namesep .. first; end end if utilities.is_set (person.link) then one = utilities.make_wikilink (person.link, one); -- link author/editor end if one then -- if <one> has a value (name, mdash replacement, or mask text replacement) local proj, tag = interwiki_prefixen_get (one, true); -- get the interwiki prefixen if present if 'w' == proj and ('Wikipedia' == mw.site.namespaces.Project['name']) then proj = nil; -- for stuff like :w:de:<article>, :w is unnecessary TODO: maint cat? end if proj then proj = ({['d'] = 'Wikidata', ['s'] = 'Wikisource', ['w'] = 'Wikipedia'})[proj]; -- :w (wikipedia) for linking from a non-wikipedia project if proj then one = one .. utilities.wrap_style ('interproj', proj); -- add resized leading space, brackets, static text, language name tag = nil; -- unset; don't do both project and language end end if tag == cfg.this_wiki_code then tag = nil; -- stuff like :en:<article> at en.wiki is pointless TODO: maint cat? end if tag then local lang = cfg.lang_code_remap[tag] or cfg.mw_languages_by_tag_t[tag]; if lang then -- error messaging done in extract_names() where we know parameter names one = one .. utilities.wrap_style ('interwiki', lang); -- add resized leading space, brackets, static text, language name end end table.insert (name_list, one); -- add it to the list of names table.insert (name_list, sep_one); -- add the proper name-list separator end end end local count = #name_list / 2; -- (number of names + number of separators) divided by 2 if 0 < count then if 1 < count and not etal then if 'amp' == format then name_list[#name_list-2] = " & "; -- replace last separator with ampersand text elseif 'and' == format then if 2 == count then name_list[#name_list-2] = cfg.presentation.sep_nl_and; -- replace last separator with 'and' text else name_list[#name_list-2] = cfg.presentation.sep_nl_end; -- replace last separator with '(sep) and' text end end end name_list[#name_list] = nil; -- erase the last separator end local result = table.concat (name_list); -- construct list if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list result = result .. sep .. ' ' .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al. end return result, count; -- return name-list string and count of number of names (count used for editor names only) end --[[--------------------< M A K E _ C I T E R E F _ I D >----------------------- Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise returns an empty string. namelist is one of the contributor-, author-, or editor-name lists chosen in that order. year is Year or anchor_year. ]] local function make_citeref_id (namelist, year) local names={}; -- a table for the one to four names and year for i,v in ipairs (namelist) do -- loop through the list and take up to the first four last names names[i] = v.last if i == 4 then break end -- if four then done end table.insert (names, year); -- add the year at the end local id = table.concat(names); -- concatenate names and year for CITEREF id if utilities.is_set (id) then -- if concatenation is not an empty string return "CITEREF" .. id; -- add the CITEREF portion else return ''; -- return an empty string; no reason to include CITEREF id in this citation end end --[[--------------------------< C I T E _ C L A S S _A T T R I B U T E _M A K E >------------------------------ construct <cite> tag class attribute for this citation. <cite_class> – config.CitationClass from calling template <mode> – value from |mode= parameter ]] local function cite_class_attribute_make (cite_class, mode) local class_t = {}; table.insert (class_t, 'citation'); -- required for blue highlight if 'citation' ~= cite_class then table.insert (class_t, cite_class); -- identify this template for user css table.insert (class_t, utilities.is_set (mode) and mode or 'cs1'); -- identify the citation style for user css or javascript else table.insert (class_t, utilities.is_set (mode) and mode or 'cs2'); -- identify the citation style for user css or javascript end for _, prop_key in ipairs (z.prop_keys_t) do table.insert (class_t, prop_key); -- identify various properties for user css or javascript end return table.concat (class_t, ' '); -- make a big string and done end --[[---------------------< N A M E _ H A S _ E T A L >-------------------------- Evaluates the content of name parameters (author, editor, etc.) for variations on the theme of et al. If found, the et al. is removed, a flag is set to true and the function returns the modified name and the flag. This function never sets the flag to false but returns its previous state because it may have been set by previous passes through this function or by the associated |display-<names>=etal parameter ]] local function name_has_etal (name, etal, nocat, param) if utilities.is_set (name) then -- name can be nil in which case just return local patterns = cfg.et_al_patterns; -- get patterns from configuration for _, pattern in ipairs (patterns) do -- loop through all of the patterns if name:match (pattern) then -- if this 'et al' pattern is found in name name = name:gsub (pattern, ''); -- remove the offending text etal = true; -- set flag (may have been set previously here or by |display-<names>=etal) if not nocat then -- no categorization for |vauthors= utilities.set_message ('err_etal', {param}); -- and set an error if not added end end end end return name, etal; end --[[---------------------< N A M E _ I S _ N U M E R I C >---------------------- Add maint cat when name parameter value does not contain letters. Does not catch mixed alphanumeric names so |last=A. Green (1922-1987) does not get caught in the current version of this test but |first=(1888) is caught. returns nothing ]] local function name_is_numeric (name, list_name) if utilities.is_set (name) then if mw.ustring.match (name, '^[%A]+$') then -- when name does not contain any letters utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template end end end --[[-----------------< N A M E _ H A S _ M U L T _ N A M E S >------------------ Evaluates the content of last/surname (authors etc.) parameters for multiple names. Multiple names are indicated if there is more than one comma or any "unescaped" semicolons. Escaped semicolons are ones used as part of selected HTML entities. If the condition is met, the function adds the multiple name maintenance category. returns nothing ]] local function name_has_mult_names (name, list_name) local _, commas, semicolons, nbsps; if utilities.is_set (name) then _, commas = name:gsub (',', ''); -- count the number of commas _, semicolons = name:gsub (';', ''); -- count the number of semicolons -- nbsps probably should be its own separate count rather than merged in -- some way with semicolons because Lua patterns do not support the -- grouping operator that regex does, which means there is no way to add -- more entities to escape except by adding more counts with the new -- entities _, nbsps = name:gsub ('&nbsp;',''); -- count nbsps -- There is exactly 1 semicolon per &nbsp; entity, so subtract nbsps -- from semicolons to 'escape' them. If additional entities are added, -- they also can be subtracted. if 1 < commas or 0 < (semicolons - nbsps) then utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message end end end --[=[-------------------------< I S _ G E N E R I C >---------------------------------------------------------- Compares values assigned to various parameters according to the string provided as <item> in the function call. <item> can have on of two values: 'generic_names' – for name-holding parameters: |last=, |first=, |editor-last=, etc 'generic_titles' – for |title= There are two types of generic tests. The 'accept' tests look for a pattern that should not be rejected by the 'reject' test. For example, |author=[[John Smith (author)|Smith, John]] would be rejected by the 'author' reject test. But piped wikilinks with 'author' disambiguation should not be rejected so the 'accept' test prevents that from happening. Accept tests are always performed before reject tests. Each of the 'accept' and 'reject' sequence tables hold tables for en.wiki (['en']) and local.wiki (['local']) that each can hold a test sequence table The sequence table holds, at index [1], a test pattern, and, at index [2], a boolean control value. The control value tells string.find() or mw.ustring.find() to do plain-text search (true) or a pattern search (false). The intent of all this complexity is to make these searches as fast as possible so that we don't run out of processing time on very large articles. Returns true when a reject test finds the pattern or string false when an accept test finds the pattern or string nil else ]=] local function is_generic (item, value, wiki) local test_val; local str_lower = { -- use string.lower() for en.wiki (['en']) and use mw.ustring.lower() or local.wiki (['local']) ['en'] = string.lower, ['local'] = mw.ustring.lower, } local str_find = { -- use string.find() for en.wiki (['en']) and use mw.ustring.find() or local.wiki (['local']) ['en'] = string.find, ['local'] = mw.ustring.find, } local function test (val, test_t, wiki) -- local function to do the testing; <wiki> selects lower() and find() functions val = test_t[2] and str_lower[wiki](value) or val; -- when <test_t[2]> set to 'true', plaintext search using lowercase value return str_find[wiki] (val, test_t[1], 1, test_t[2]); -- return nil when not found or matched end local test_types_t = {'accept', 'reject'}; -- test accept patterns first, then reject patterns local wikis_t = {'en', 'local'}; -- do tests for each of these keys; en.wiki first, local.wiki second for _, test_type in ipairs (test_types_t) do -- for each test type for _, generic_value in pairs (cfg.special_case_translation[item][test_type]) do -- spin through the list of generic value fragments to accept or reject for _, wiki in ipairs (wikis_t) do if generic_value[wiki] then if test (value, generic_value[wiki], wiki) then -- go do the test return ('reject' == test_type); -- param value rejected, return true; false else end end end end end end --[[--------------------------< N A M E _ I S _ G E N E R I C >------------------------------------------------ calls is_generic() to determine if <name> is a 'generic name' listed in cfg.generic_names; <name_alias> is the parameter name used in error messaging ]] local function name_is_generic (name, name_alias) if not added_generic_name_errs and is_generic ('generic_names', name) then utilities.set_message ('err_generic_name', name_alias); -- set an error message added_generic_name_errs = true; end end --[[--------------------------< N A M E _ C H E C K S >-------------------------------------------------------- This function calls various name checking functions used to validate the content of the various name-holding parameters. ]] local function name_checks (last, first, list_name, last_alias, first_alias) local accept_name; if utilities.is_set (last) then last, accept_name = utilities.has_accept_as_written (last); -- remove accept-this-as-written markup when it wraps all of <last> if not accept_name then -- <last> not wrapped in accept-as-written markup name_has_mult_names (last, list_name); -- check for multiple names in the parameter (last only) name_is_numeric (last, list_name); -- check for names that are composed of digits and punctuation name_is_generic (last, last_alias); -- check for names found in the generic names list end end if utilities.is_set (first) then first, accept_name = utilities.has_accept_as_written (first); -- remove accept-this-as-written markup when it wraps all of <first> if not accept_name then -- <first> not wrapped in accept-as-written markup name_is_numeric (first, list_name); -- check for names that are composed of digits and punctuation name_is_generic (first, first_alias); -- check for names found in the generic names list end local wl_type, D = utilities.is_wikilink (first); if 0 ~= wl_type then first = D; utilities.set_message ('err_bad_paramlink', first_alias); end end return last, first; -- done end --[[----------------------< E X T R A C T _ N A M E S >------------------------- Gets name list from the input arguments Searches through args in sequential order to find |lastn= and |firstn= parameters (or their aliases), and their matching link and mask parameters. Stops searching when both |lastn= and |firstn= are not found in args after two sequential attempts: found |last1=, |last2=, and |last3= but doesn't find |last4= and |last5= then the search is done. This function emits an error message when there is a |firstn= without a matching |lastn=. When there are 'holes' in the list of last names, |last1= and |last3= are present but |last2= is missing, an error message is emitted. |lastn= is not required to have a matching |firstn=. When an author or editor parameter contains some form of 'et al.', the 'et al.' is stripped from the parameter and a flag (etal) returned that will cause list_people() to add the static 'et al.' text from Module:Citation/CS1/Configuration. This keeps 'et al.' out of the template's metadata. When this occurs, an error is emitted. ]] local function extract_names(args, list_name) local names = {}; -- table of names local last; -- individual name components local first; local link; local mask; local i = 1; -- loop counter/indexer local n = 1; -- output table indexer local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors) local etal = false; -- return value set to true when we find some form of et al. in an author parameter local last_alias, first_alias, link_alias; -- selected parameter aliases used in error messaging while true do last, last_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'err_redundant_parameters', i ); -- search through args for name components beginning at 1 first, first_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'err_redundant_parameters', i ); link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ); mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i ); if last then -- error check |lastn= alias for unknown interwiki link prefix; done here because this is where we have the parameter name local project, language = interwiki_prefixen_get (last, true); -- true because we expect interwiki links in |lastn= to be wikilinked if nil == project and nil == language then -- when both are nil utilities.set_message ('err_bad_paramlink', last_alias); -- not known, emit an error message -- TODO: err_bad_interwiki? last = utilities.remove_wiki_link (last); -- remove wikilink markup; show display value only end end if link then -- error check |linkn= alias for unknown interwiki link prefix local project, language = interwiki_prefixen_get (link, false); -- false because wiki links in |author-linkn= is an error if nil == project and nil == language then -- when both are nil utilities.set_message ('err_bad_paramlink', link_alias); -- not known, emit an error message -- TODO: err_bad_interwiki? link = nil; -- unset so we don't link link_alias = nil; end end last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al. first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al. last, first = name_checks (last, first, list_name, last_alias, first_alias); -- multiple names, extraneous annotation, etc. checks if first and not last then -- if there is a firstn without a matching lastn local alias = first_alias:find ('given', 1, true) and 'given' or 'first'; -- get first or given form of the alias utilities.set_message ('err_first_missing_last', { first_alias, -- param name of alias missing its mate first_alias:gsub (alias, {['first'] = 'last', ['given'] = 'surname'}), -- make param name appropriate to the alias form }); -- add this error message elseif not first and not last then -- if both firstn and lastn aren't found, are we done? count = count + 1; -- number of times we haven't found last and first if 2 <= count then -- two missing names and we give up break; -- normal exit or there is a two-name hole in the list; can't tell which end else -- we have last with or without a first local result; link = link_title_ok (link, link_alias, last, last_alias); -- check for improper wiki-markup if first then link = link_title_ok (link, link_alias, first, first_alias); -- check for improper wiki-markup end names[n] = {last = last, first = first, link = link, mask = mask, corporate = false}; -- add this name to our names list (corporate for |vauthors= only) n = n + 1; -- point to next location in the names table if 1 == count then -- if the previous name was missing utilities.set_message ('err_missing_name', {list_name:match ("(%w+)List"):lower(), i - 1}); -- add this error message end count = 0; -- reset the counter, we're looking for two consecutive missing names end i = i + 1; -- point to next args location end return names, etal; -- all done, return our list of names and the etal flag end --[[--------------------------< N A M E _ T A G _ G E T >------------------------------------------------------ attempt to decode |language=<lang_param> and return language name and matching tag; nil else. This function looks for: <lang_param> as a tag in cfg.lang_code_remap{} <lang_param> as a name in cfg.lang_name_remap{} <lang_param> as a name in cfg.mw_languages_by_name_t <lang_param> as a tag in cfg.mw_languages_by_tag_t when those fail, presume that <lang_param> is an IETF-like tag that MediaWiki does not recognize. Strip all script, region, variant, whatever subtags from <lang_param> to leave just a two or three character language tag and look for the new <lang_param> in cfg.mw_languages_by_tag_t{} on success, returns name (in properly capitalized form) and matching tag (in lowercase); on failure returns nil ]] local function name_tag_get (lang_param) local lang_param_lc = mw.ustring.lower (lang_param); -- use lowercase as an index into the various tables local name; local tag; name = cfg.lang_code_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name if name then -- when <name>, <lang_param> is a tag for a remapped language name return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc> end tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags name = cfg.lang_code_remap[tag]; -- attempt to get remapped language name with language subtag only if name then -- when <name>, <tag> is a tag for a remapped language name return name, tag; -- so return <name> from remap and <tag> end if cfg.lang_name_remap[lang_param_lc] then -- not a tag, assume <lang_param_lc> is a name; attempt to get remapped language tag return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag end tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag if tag then return cfg.mw_languages_by_tag_t[tag], tag; -- <lang_param_lc> is a name so return the name from the table and <tag> end name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name if name then return name, lang_param_lc; -- <lang_param_lc> is a tag so return it and <name> end tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else if tag then name = cfg.mw_languages_by_tag_t[tag]; -- attempt to get a language name using the shortened <tag> if name then return name, tag; -- <lang_param_lc> is an unrecognized IETF-like tag so return <name> and language subtag end end end --[[-------------------< L A N G U A G E _ P A R A M E T E R >------------------ Gets language name from a provided two- or three-character ISO 639 code. If a code is recognized by MediaWiki, use the returned name; if not, then use the value that was provided with the language parameter. When |language= contains a recognized language (either code or name), the page is assigned to the category for that code: Category:Norwegian-language sources (no). For valid three-character code languages, the page is assigned to the single category for '639-2' codes: Category:CS1 ISO 639-2 language sources. Languages that are the same as the local wiki are not categorized. MediaWiki does not recognize three-character equivalents of two-character codes: code 'ar' is recognized but code 'ara' is not. This function supports multiple languages in the form |language=nb, French, th where the language names or codes are separated from each other by commas with optional space characters. ]] local function language_parameter (lang) local tag; -- some form of IETF-like language tag; language subtag with optional region, sript, vatiant, etc subtags local lang_subtag; -- ve populates |language= with mostly unecessary region subtags the MediaWiki does not recognize; this is the base language subtag local name; -- the language name local language_list = {}; -- table of language names to be rendered local names_t = {}; -- table made from the value assigned to |language= local this_wiki_name = mw.language.fetchLanguageName (cfg.this_wiki_code, cfg.this_wiki_code); -- get this wiki's language name names_t = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>; <name> has proper capitalization; <tag> is lowercase if utilities.is_set (tag) then lang_subtag = tag:gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language if 2 == lang_subtag:len() then -- and is a two-character tag utilities.add_prop_cat ('foreign-lang-source', {name, tag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization else -- or is a recognized language (but has a three-character tag) utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template? end elseif cfg.local_lang_cat_enable then -- when the language and this wiki's language are the same and categorization is enabled utilities.add_prop_cat ('local-lang-source', {name, lang_subtag}); -- categorize it end else name = lang; -- return whatever <lang> has so that we show something utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added end table.insert (language_list, name); name = ''; -- so we can reuse it end name = utilities.make_sep_list (#language_list, language_list); if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then -- when only one language, find lang name in this wiki lang name; for |language=en-us, 'English' in 'American English' return ''; -- if one language and that language is this wiki's return an empty string (no annotation) end return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)' --[[ TODO: should only return blank or name rather than full list so we can clean up the bunched parenthetical elements Language, Type, Format ]] end --[[-----------------------< S E T _ C S _ S T Y L E >-------------------------- Gets the default CS style configuration for the given mode. Returns default separator and either postscript as passed in or the default. In CS1, the default postscript and separator are '.'. In CS2, the default postscript is the empty string and the default separator is ','. ]] local function set_cs_style (postscript, mode) if utilities.is_set(postscript) then -- emit a maintenance message if user postscript is the default cs1 postscript -- we catch the opposite case for cs2 in set_style if mode == 'cs1' and postscript == cfg.presentation['ps_' .. mode] then utilities.set_message ('maint_postscript'); end else postscript = cfg.presentation['ps_' .. mode]; end return cfg.presentation['sep_' .. mode], postscript; end --[[--------------------------< S E T _ S T Y L E >----------------------------- Sets the separator and postscript styles. Checks the |mode= first and the #invoke CitationClass second. Removes the postscript if postscript == none. ]] local function set_style (mode, postscript, cite_class) local sep; if 'cs2' == mode then sep, postscript = set_cs_style (postscript, 'cs2'); elseif 'cs1' == mode then sep, postscript = set_cs_style (postscript, 'cs1'); elseif 'citation' == cite_class then sep, postscript = set_cs_style (postscript, 'cs2'); else sep, postscript = set_cs_style (postscript, 'cs1'); end if cfg.keywords_xlate[postscript:lower()] == 'none' then -- emit a maintenance message if user postscript is the default cs2 postscript -- we catch the opposite case for cs1 in set_cs_style if 'cs2' == mode or 'citation' == cite_class then utilities.set_message ('maint_postscript'); end postscript = ''; end return sep, postscript end --[=[-------------------------< I S _ P D F >----------------------------------- Determines if a URL has the file extension that is one of the PDF file extensions used by [[MediaWiki:Common.css]] when applying the PDF icon to external links. returns true if file extension is one of the recognized extensions, else false ]=] local function is_pdf (url) return url:match ('%.pdf$') or url:match ('%.PDF$') or url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or url:match ('%.PDF&#035') or url:match ('%.pdf&#035'); end --[[--------------------------< S T Y L E _ F O R M A T >----------------------- Applies CSS style to |format=, |chapter-format=, etc. Also emits an error message if the format parameter does not have a matching URL parameter. If the format parameter is not set and the URL contains a file extension that is recognized as a PDF document by MediaWiki's commons.css, this code will set the format parameter to (PDF) with the appropriate styling. ]] local function style_format (format, url, fmt_param, url_param) if utilities.is_set (format) then format = utilities.wrap_style ('format', format); -- add leading space, parentheses, resize if not utilities.is_set (url) then utilities.set_message ('err_format_missing_url', {fmt_param, url_param}); -- add an error message end elseif is_pdf (url) then -- format is not set so if URL is a PDF file then format = utilities.wrap_style ('format', 'PDF'); -- set format to PDF else format = ''; -- empty string for concatenation end return format; end --[[---------------------< G E T _ D I S P L A Y _ N A M E S >------------------ Returns a number that defines the number of names displayed for author and editor name lists and a Boolean flag to indicate when et al. should be appended to the name list. When the value assigned to |display-xxxxors= is a number greater than or equal to zero, return the number and the previous state of the 'etal' flag (false by default but may have been set to true if the name list contains some variant of the text 'et al.'). When the value assigned to |display-xxxxors= is the keyword 'etal', return a number that is one greater than the number of authors in the list and set the 'etal' flag true. This will cause the list_people() to display all of the names in the name list followed by 'et al.' In all other cases, returns nil and the previous state of the 'etal' flag. inputs: max: A['DisplayAuthors'] or A['DisplayEditors']; a number or some flavor of etal count: #a or #e list_name: 'authors' or 'editors' etal: author_etal or editor_etal ]] local function get_display_names (max, count, list_name, etal, param) if utilities.is_set (max) then if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings max = count + 1; -- number of authors + 1 so display all author name plus et al. etal = true; -- overrides value set by extract_names() elseif max:match ('^%d+$') then -- if is a string of numbers max = tonumber (max); -- make it a number if max >= count then -- if |display-xxxxors= value greater than or equal to number of authors/editors utilities.set_message ('err_disp_name', {param, max}); -- add error message max = nil; end else -- not a valid keyword or number utilities.set_message ('err_disp_name', {param, max}); -- add error message max = nil; -- unset; as if |display-xxxxors= had not been set end end return max, etal; end --[[----------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >--------------- Adds error if |page=, |pages=, |quote-page=, |quote-pages= has what appears to be some form of p. or pp. abbreviation in the first characters of the parameter content. check page for extraneous p, p., pp, pp., pg, pg. at start of parameter value: good pattern: '^P[^%.P%l]' matches when page begins PX or P# but not Px where x and X are letters and # is a digit bad pattern: '^[Pp][PpGg]' matches when page begins pp, pP, Pp, PP, pg, pG, Pg, PG ]] local function extra_text_in_page_check (val, name) if not val:match (cfg.vol_iss_pg_patterns.good_ppattern) then for _, pattern in ipairs (cfg.vol_iss_pg_patterns.bad_ppatterns) do -- spin through the selected sequence table of patterns if val:match (pattern) then -- when a match, error so utilities.set_message ('err_extra_text_pages', name); -- add error message return; -- and done end end end end --[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------ Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator. For |volume=: 'V.', or 'Vol.' (with or without the dot) abbreviations or 'Volume' in the first characters of the parameter content (all case insensitive). 'V' and 'v' (without the dot) are presumed to be roman numerals so are allowed. For |issue=: 'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the parameter content (all case insensitive). Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or whitespace character) – param values are trimmed of whitespace by MediaWiki before delivered to the module. <val> is |volume= or |issue= parameter value <name> is |volume= or |issue= parameter name for error message <selector> is 'v' for |volume=, 'i' for |issue= sets error message on failure; returns nothing ]] local function extra_text_in_vol_iss_check (val, name, selector) if not utilities.is_set (val) then return; end local patterns = 'v' == selector and cfg.vol_iss_pg_patterns.vpatterns or cfg.vol_iss_pg_patterns.ipatterns; local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue'; val = val:lower(); -- force parameter value to lower case for _, pattern in ipairs (patterns) do -- spin through the selected sequence table of patterns if val:match (pattern) then -- when a match, error so utilities.set_message (handler, name); -- add error message return; -- and done end end end --[=[-------------------------< G E T _ V _ N A M E _ T A B L E >---------------------------------------------- split apart a |vauthors= or |veditors= parameter. This function allows for corporate names, wrapped in doubled parentheses to also have commas; in the old version of the code, the doubled parentheses were included in the rendered citation and in the metadata. Individual author names may be wikilinked |vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.)) ]=] local function get_v_name_table (vparam, output_table, output_link_table) local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas local wl_type, label, link; -- wl_type not used here; just a placeholder local i = 1; while name_table[i] do if name_table[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of corporate with one or more commas; this segment has the opening doubled parentheses local name = name_table[i]; i = i + 1; -- bump indexer to next segment while name_table[i] do name = name .. ', ' .. name_table[i]; -- concatenate with previous segments if name_table[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parentheses break; -- and done reassembling so end i = i + 1; -- bump indexer end table.insert (output_table, name); -- and add corporate name to the output table table.insert (output_link_table, ''); -- no wikilink else wl_type, label, link = utilities.is_wikilink (name_table[i]); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]] table.insert (output_table, label); -- add this name if 1 == wl_type then table.insert (output_link_table, label); -- simple wikilink [[D]] else table.insert (output_link_table, link); -- no wikilink or [[L|D]]; add this link if there is one, else empty string end end i = i + 1; end return output_table; end --[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >-------------------------------- This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and |xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does. Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance tests, are wrapped in doubled parentheses ((corporate name)) to suppress the format tests. Supports generational suffixes Jr, 2nd, 3rd, 4th–6th. This function sets the Vancouver error when a required comma is missing and when there is a space between an author's initials. ]] local function parse_vauthors_veditors (args, vparam, list_name) local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn= local v_name_table = {}; local v_link_table = {}; -- when name is wikilinked, targets go in this table local etal = false; -- return value set to true when we find some form of et al. vauthors parameter local last, first, link, mask, suffix; local corporate = false; vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period) v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas for i, v_name in ipairs(v_name_table) do first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor local accept_name; v_name, accept_name = utilities.has_accept_as_written (v_name); -- remove accept-this-as-written markup when it wraps all of <v_name> if accept_name then last = v_name; corporate = true; -- flag used in list_people() elseif string.find(v_name, "%s") then if v_name:find('[;%.]') then -- look for commonly occurring punctuation characters; add_vanc_error (cfg.err_msg_supl.punctuation, i); end local lastfirstTable = {} lastfirstTable = mw.text.split(v_name, "%s+") first = table.remove(lastfirstTable); -- removes and returns value of last element in table which should be initials or generational suffix if not mw.ustring.match (first, '^%u+$') then -- mw.ustring here so that later we will catch non-Latin characters suffix = first; -- not initials so assume that whatever we got is a generational suffix first = table.remove(lastfirstTable); -- get what should be the initials from the table end last = table.concat(lastfirstTable, ' ') -- returns a string that is the concatenation of all other names that are not initials and generational suffix if not utilities.is_set (last) then first = ''; -- unset last = v_name; -- last empty because something wrong with first add_vanc_error (cfg.err_msg_supl.name, i); end if mw.ustring.match (last, '%a+%s+%u+%s+%a+') then add_vanc_error (cfg.err_msg_supl['missing comma'], i); -- matches last II last; the case when a comma is missing end if mw.ustring.match (v_name, ' %u %u$') then -- this test is in the wrong place TODO: move or replace with a more appropriate test add_vanc_error (cfg.err_msg_supl.initials, i); -- matches a space between two initials end else last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this? end if utilities.is_set (first) then if not mw.ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else add_vanc_error (cfg.err_msg_supl.initials, i); -- too many initials; mixed case initials (which may be ok Romanization); hyphenated initials end is_good_vanc_name (last, first, suffix, i); -- check first and last before restoring the suffix which may have a non-Latin digit if utilities.is_set (suffix) then first = first .. ' ' .. suffix; -- if there was a suffix concatenate with the initials suffix = ''; -- unset so we don't add this suffix to all subsequent names end else if not corporate then is_good_vanc_name (last, '', nil, i); end end link = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ) or v_link_table[i]; mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i ); names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate}; -- add this assembled name to our names list end return names, etal; -- all done, return our list of names end --[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------ Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or select one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list. Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest; |editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn) When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second test which mimicks the test used in extract_names() when looking for a hole in the author name list. There may be a better way to do this, I just haven't discovered what that way is. Emits an error message when more than one xxxxor name source is provided. In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate. ]] local function select_author_editor_source (vxxxxors, xxxxors, args, list_name) local lastfirst = false; if utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 1 ) or -- do this twice in case we have a |first1= without a |last1=; this ... utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 1 ) or -- ... also catches the case where |first= is used with |vauthors= utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 2 ) or utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 2 ) then lastfirst = true; end if (utilities.is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions (utilities.is_set (vxxxxors) and utilities.is_set (xxxxors)) or (true == lastfirst and utilities.is_set (xxxxors)) then local err_name; if 'AuthorList' == list_name then -- figure out which name should be used in error message err_name = 'author'; else err_name = 'editor'; end utilities.set_message ('err_redundant_parameters', err_name .. '-name-list parameters'); -- add error message end if true == lastfirst then return 1 end; -- return a number indicating which author name source to use if utilities.is_set (vxxxxors) then return 2 end; if utilities.is_set (xxxxors) then return 3 end; return 1; -- no authors so return 1; this allows missing author name test to run in case there is a first without last end --[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------ This function is used to validate a parameter's assigned value for those parameters that have only a limited number of allowable values (yes, y, true, live, dead, etc.). When the parameter value has not been assigned a value (missing or empty in the source template) the function returns the value specified by ret_val. If the parameter value is one of the list of allowed values returns the translated value; else, emits an error message and returns the value specified by ret_val. TODO: explain <invert> ]] local function is_valid_parameter_value (value, name, possible, ret_val, invert) if not utilities.is_set (value) then return ret_val; -- an empty parameter is ok end if (not invert and utilities.in_array (value, possible)) then -- normal; <value> is in <possible> table return cfg.keywords_xlate[value]; -- return translation of parameter keyword elseif invert and not utilities.in_array (value, possible) then -- invert; <value> is not in <possible> table return value; -- return <value> as it is else utilities.set_message ('err_invalid_param_val', {name, value}); -- not an allowed value so add error message return ret_val; end end --[[--------------------------< T E R M I N A T E _ N A M E _ L I S T >---------------------------------------- This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a space when the last character is not a sepc character or when the last three characters are not sepc followed by two closing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with a single space character. ]] local function terminate_name_list (name_list, sepc) if (string.sub (name_list, -3, -1) == sepc .. '. ') then -- if already properly terminated return name_list; -- just return the name list elseif (string.sub (name_list, -1, -1) == sepc) or (string.sub (name_list, -3, -1) == sepc .. ']]') then -- if last name in list ends with sepc char return name_list .. " "; -- don't add another else return name_list .. sepc .. ' '; -- otherwise terminate the name list end end --[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >----------------------------------------- returns the concatenation of the formatted volume and issue (or journal article number) parameters as a single string; or formatted volume or formatted issue, or an empty string if neither are set. ]] local function format_volume_issue (volume, issue, article, cite_class, origin, sepc, lower) if not utilities.is_set (volume) and not utilities.is_set (issue) and not utilities.is_set (article) then return ''; end -- same condition as in format_pages_sheets() local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin); local is_numeric_vol = volume and (volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$')); -- is only uppercase roman numerals or only digits? local is_long_vol = volume and (4 < mw.ustring.len(volume)); -- is |volume= value longer than 4 characters? if volume and (not is_numeric_vol and is_long_vol) then -- when not all digits or Roman numerals, is |volume= longer than 4 characters? utilities.add_prop_cat ('long-vol'); -- yes, add properties cat end if is_journal then -- journal-style formatting local vol = ''; if utilities.is_set (volume) then if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals? vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, volume}); -- render in bold face elseif is_long_vol then -- not all digits or Roman numerals; longer than 4 characters? vol = utilities.substitute (cfg.messages['j-vol'], {sepc, utilities.hyphen_to_dash (volume)}); -- not bold else -- four or fewer characters vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, utilities.hyphen_to_dash (volume)}); -- bold end end vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '') vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '') return vol; end if 'podcast' == cite_class and utilities.is_set (issue) then return wrap_msg ('issue', {sepc, issue}, lower); end if 'conference' == cite_class and utilities.is_set (article) then -- |article-number= supported only in journal and conference cites if utilities.is_set (volume) and utilities.is_set (article) then -- both volume and article number return wrap_msg ('vol-art', {sepc, utilities.hyphen_to_dash (volume), article}, lower); elseif utilities.is_set (article) then -- article number alone; when volume alone, handled below return wrap_msg ('art', {sepc, article}, lower); end end -- all other types of citation if utilities.is_set (volume) and utilities.is_set (issue) then return wrap_msg ('vol-no', {sepc, utilities.hyphen_to_dash (volume), issue}, lower); elseif utilities.is_set (volume) then return wrap_msg ('vol', {sepc, utilities.hyphen_to_dash (volume)}, lower); else return wrap_msg ('issue', {sepc, issue}, lower); end end --[[-------------------------< F O R M A T _ P A G E S _ S H E E T S >----------------------------------------- adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings. The return order is: page, pages, sheet, sheets Singular has priority over plural when both are provided. ]] local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower) if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators if utilities.is_set (sheet) then if 'journal' == origin then return '', '', wrap_msg ('j-sheet', sheet, lower), ''; else return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), ''; end elseif utilities.is_set (sheets) then if 'journal' == origin then return '', '', '', wrap_msg ('j-sheets', sheets, lower); else return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower); end end end local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin); if utilities.is_set (page) then if is_journal then return utilities.substitute (cfg.messages['j-page(s)'], page), '', '', ''; elseif not nopp then return utilities.substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', ''; else return utilities.substitute (cfg.messages['nopp'], {sepc, page}), '', '', ''; end elseif utilities.is_set (pages) then if is_journal then return utilities.substitute (cfg.messages['j-page(s)'], pages), '', '', ''; elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number return '', utilities.substitute (cfg.messages['p-prefix'], {sepc, pages}), '', ''; elseif not nopp then return '', utilities.substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', ''; else return '', utilities.substitute (cfg.messages['nopp'], {sepc, pages}), '', ''; end end return '', '', '', ''; -- return empty strings end --[[--------------------------< I N S O U R C E _ L O C _ G E T >---------------------------------------------- returns one of the in-source locators: page, pages, or at. If any of these are interwiki links to Wikisource, returns the label portion of the interwiki-link as plain text for use in COinS. This COinS thing is done because here we convert an interwiki-link to an external link and add an icon span around that; get_coins_pages() doesn't know about the span. TODO: should it? TODO: add support for sheet and sheets?; streamline; TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned to a new name)? ]] local function insource_loc_get (page, page_orig, pages, pages_orig, at) local ws_url, ws_label, coins_pages, L; -- for Wikisource interwiki-links; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?) if utilities.is_set (page) then if utilities.is_set (pages) or utilities.is_set (at) then pages = ''; -- unset the others at = ''; end extra_text_in_page_check (page, page_orig); -- emit error message when |page= value begins with what looks like p., pp., etc. ws_url, ws_label, L = wikisource_url_make (page); -- make ws URL from |page= interwiki link; link portion L becomes tooltip label if ws_url then page = external_link (ws_url, ws_label .. '&nbsp;', 'ws link in page'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? page = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page}); coins_pages = ws_label; end elseif utilities.is_set (pages) then if utilities.is_set (at) then at = ''; -- unset end extra_text_in_page_check (pages, pages_orig); -- emit error message when |page= value begins with what looks like p., pp., etc. ws_url, ws_label, L = wikisource_url_make (pages); -- make ws URL from |pages= interwiki link; link portion L becomes tooltip label if ws_url then pages = external_link (ws_url, ws_label .. '&nbsp;', 'ws link in pages'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? pages = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages}); coins_pages = ws_label; end elseif utilities.is_set (at) then ws_url, ws_label, L = wikisource_url_make (at); -- make ws URL from |at= interwiki link; link portion L becomes tooltip label if ws_url then at = external_link (ws_url, ws_label .. '&nbsp;', 'ws link in at'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? at = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at}); coins_pages = ws_label; end end return page, pages, at, coins_pages; end --[[--------------------------< I S _ U N I Q U E _ A R C H I V E _ U R L >------------------------------------ add error message when |archive-url= value is same as |url= or chapter-url= (or alias...) value ]] local function is_unique_archive_url (archive, url, c_url, source, date) if utilities.is_set (archive) then if archive == url or archive == c_url then utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); -- add error message return '', ''; -- unset |archive-url= and |archive-date= because same as |url= or |chapter-url= end end return archive, date; end --[=[-------------------------< A R C H I V E _ U R L _ C H E C K >-------------------------------------------- Check archive.org URLs to make sure they at least look like they are pointing at valid archives and not to the save snapshot URL or to calendar pages. When the archive URL is 'https://web.archive.org/save/' (or http://...) archive.org saves a snapshot of the target page in the URL. That is something that Wikipedia should not allow unwitting readers to do. When the archive.org URL does not have a complete timestamp, archive.org chooses a snapshot according to its own algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results. This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and |archive-date= and an error message when: |archive-url= holds an archive.org save command URL |archive-url= is an archive.org URL that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the correct place otherwise returns |archive-url= and |archive-date= There are two mostly compatible archive.org URLs: //web.archive.org/<timestamp>... -- the old form //web.archive.org/web/<timestamp>... -- the new form The old form does not support or map to the new form when it contains a display flag. There are four identified flags ('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore) we don't check for these specific flags but we do check the form. This function supports a preview mode. When the article is rendered in preview mode, this function may return a modified archive URL: for save command errors, return undated wildcard (/*/) for timestamp errors when the timestamp has a wildcard, return the URL unmodified for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/) ]=] local function archive_url_check (url, date) local err_msg = ''; -- start with the error message empty local path, timestamp, flag; -- portions of the archive.org URL if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate end if url:match('//web%.archive%.org/save/') then -- if a save command URL, we don't want to allow saving of the target page err_msg = cfg.err_msg_supl.save; url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL elseif url:match('//liveweb%.archive%.org/') then err_msg = cfg.err_msg_supl.liveweb; else path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the URL parts for evaluation if not path then -- malformed in some way; pattern did not match err_msg = cfg.err_msg_supl.timestamp; elseif 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here err_msg = cfg.err_msg_supl.timestamp; if '*' ~= flag then local replacement = timestamp:match ('^%d%d%d%d%d%d') or timestamp:match ('^%d%d%d%d'); -- get the first 6 (YYYYMM) or first 4 digits (YYYY) if replacement then -- nil if there aren't at least 4 digits (year) replacement = replacement .. string.rep ('0', 14 - replacement:len()); -- year or yearmo (4 or 6 digits) zero-fill to make 14-digit timestamp url=url:gsub ('(//web%.archive%.org/[^%d]*)%d[^/]*', '%1' .. replacement .. '*', 1) -- for preview, modify ts to 14 digits plus splat for calendar display end end elseif utilities.is_set (path) and 'web/' ~= path then -- older archive URLs do not have the extra 'web/' path element err_msg = cfg.err_msg_supl.path; elseif utilities.is_set (flag) and not utilities.is_set (path) then -- flag not allowed with the old form URL (without the 'web/' path element) err_msg = cfg.err_msg_supl.flag; elseif utilities.is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element) err_msg = cfg.err_msg_supl.flag; else return url, date; -- return ArchiveURL and ArchiveDate end end -- if here, something not right so utilities.set_message ('err_archive_url', {err_msg}); -- add error message and if is_preview_mode then return url, date; -- preview mode so return ArchiveURL and ArchiveDate else return '', ''; -- return empty strings for ArchiveURL and ArchiveDate end end --[[--------------------------< P L A C E _ C H E C K >-------------------------------------------------------- check |place=, |publication-place=, |location= to see if these params include digits. This function added because many editors misuse location to specify the in-source location (|page(s)= and |at= are supposed to do that) returns the original parameter value without modification; added maint cat when parameter value contains digits ]] local function place_check (param_val) if not utilities.is_set (param_val) then -- parameter empty or omitted return param_val; -- return that empty state end if mw.ustring.find (param_val, '%d') then -- not empty, are there digits in the parameter value utilities.set_message ('maint_location'); -- yep, add maint cat end return param_val; -- and done end --[[--------------------------< I S _ A R C H I V E D _ C O P Y >---------------------------------------------- compares |title= to 'Archived copy' (placeholder added by bots that can't find proper title); if matches, return true; nil else ]] local function is_archived_copy (title) title = mw.ustring.lower(title); -- switch title to lower case if title:find (cfg.special_case_translation.archived_copy.en) then -- if title is 'Archived copy' return true; elseif cfg.special_case_translation.archived_copy['local'] then if mw.ustring.find (title, cfg.special_case_translation.archived_copy['local']) then -- mw.ustring() because might not be Latin script return true; end end end --[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ This is the main function doing the majority of the citation formatting. ]] local function citation0( config, args ) --[[ Load Input Parameters The argument_wrapper facilitates the mapping of multiple aliases to single internal variable. ]] local A = argument_wrapper ( args ); local i -- Pick out the relevant fields from the arguments. Different citation templates -- define different field names for the same underlying things. local author_etal; local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors= local Authors; local NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], ''); local Collaboration = A['Collaboration']; do -- to limit scope of selected local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList'); if 1 == selected then a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn= elseif 2 == selected then NameListStyle = 'vanc'; -- override whatever |name-list-style= might be a, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn= elseif 3 == selected then Authors = A['Authors']; -- use content of |authors= if 'authors' == A:ORIGIN('Authors') then -- but add a maint cat if the parameter is |authors= utilities.set_message ('maint_authors'); -- because use of this parameter is discouraged; what to do about the aliases is a TODO: end end if utilities.is_set (Collaboration) then author_etal = true; -- so that |display-authors=etal not required end end local editor_etal; local e = {}; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors= do -- to limit scope of selected local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList'); -- support for |editors= withdrawn if 1 == selected then e, editor_etal = extract_names (args, 'EditorList'); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn= elseif 2 == selected then NameListStyle = 'vanc'; -- override whatever |name-list-style= might be e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn= end end local Chapter = A['Chapter']; -- done here so that we have access to |contribution= from |chapter= aliases local Chapter_origin = A:ORIGIN ('Chapter'); local Contribution; -- because contribution is required for contributor(s) if 'contribution' == Chapter_origin then Contribution = Chapter; -- get the name of the contribution end local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (A['Periodical']) then -- |contributor= and |contribution= only supported in book cites c = extract_names (args, 'ContributorList'); -- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn= if 0 < #c then if not utilities.is_set (Contribution) then -- |contributor= requires |contribution= utilities.set_message ('err_contributor_missing_required_param', 'contribution'); -- add missing contribution error message c = {}; -- blank the contributors' table; it is used as a flag later end if 0 == #a then -- |contributor= requires |author= utilities.set_message ('err_contributor_missing_required_param', 'author'); -- add missing author error message c = {}; -- blank the contributors' table; it is used as a flag later end end else -- if not a book cite if utilities.select_one (args, cfg.aliases['ContributorList-Last'], 'err_redundant_parameters', 1 ) then -- are there contributor name list parameters? utilities.set_message ('err_contributor_ignored'); -- add contributor ignored error message end Contribution = nil; -- unset end local Title = A['Title']; local TitleLink = A['TitleLink']; local auto_select = ''; -- default is auto local accept_link; TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup if (not accept_link) and utilities.in_array (TitleLink, {'none', 'pmc', 'doi'}) then -- check for special keywords auto_select = TitleLink; -- remember selection for later TitleLink = ''; -- treat as if |title-link= would have been empty end TitleLink = link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title'); -- check for wiki-markup in |title-link= or wiki-markup in |title= when |title-link= is set local Section = ''; -- {{cite map}} only; preset to empty string for concatenation if not used if 'map' == config.CitationClass and 'section' == Chapter_origin then Section = A['Chapter']; -- get |section= from |chapter= alias list; |chapter= and the other aliases not supported in {{cite map}} Chapter = ''; -- unset for now; will be reset later from |map= if present end local Periodical = A['Periodical']; local Periodical_origin = ''; if utilities.is_set (Periodical) then Periodical_origin = A:ORIGIN('Periodical'); -- get the name of the periodical parameter local i; Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated if i then -- non-zero when markup was stripped so emit an error message utilities.set_message ('err_apostrophe_markup', {Periodical_origin}); end end if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}} if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'mailinglist')}); end Periodical = A ['MailingList']; -- error or no, set Periodical to |mailinglist= value because this template is {{cite mailing list}} Periodical_origin = A:ORIGIN('MailingList'); end local ScriptPeriodical = A['ScriptPeriodical']; -- web and news not tested for now because of -- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_"Cite_Web"_errors? if not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) then -- 'periodical' templates require periodical parameter -- local p = {['journal'] = 'journal', ['magazine'] = 'magazine', ['news'] = 'newspaper', ['web'] = 'website'}; -- for error message local p = {['journal'] = 'journal', ['magazine'] = 'magazine'}; -- for error message if p[config.CitationClass] then utilities.set_message ('err_missing_periodical', {config.CitationClass, p[config.CitationClass]}); end end local Volume; local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical'); if 'citation' == config.CitationClass then if utilities.is_set (Periodical) then if not utilities.in_array (Periodical_origin, cfg.citation_no_volume_t) then -- {{citation}} does not render |volume= when these parameters are used Volume = A['Volume']; -- but does for all other 'periodicals' end elseif utilities.is_set (ScriptPeriodical) then if 'script-website' ~= ScriptPeriodical_origin then -- {{citation}} does not render volume for |script-website= Volume = A['Volume']; -- but does for all other 'periodicals' end else Volume = A['Volume']; -- and does for non-'periodical' cites end elseif utilities.in_array (config.CitationClass, cfg.templates_using_volume) then -- render |volume= for cs1 according to the configuration settings Volume = A['Volume']; end extra_text_in_vol_iss_check (Volume, A:ORIGIN ('Volume'), 'v'); local Issue; if 'citation' == config.CitationClass then if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, cfg.citation_issue_t) then -- {{citation}} may render |issue= when these parameters are used Issue = utilities.hyphen_to_dash (A['Issue']); end elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table if not (utilities.in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical))) then Issue = utilities.hyphen_to_dash (A['Issue']); end end local ArticleNumber; if utilities.in_array (config.CitationClass, {'journal', 'conference'}) or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then ArticleNumber = A['ArticleNumber']; end extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i'); local Page; local Pages; local At; local QuotePage; local QuotePages; if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then -- TODO: rewrite to emit ignored parameter error message? Page = A['Page']; Pages = utilities.hyphen_to_dash (A['Pages']); At = A['At']; QuotePage = A['QuotePage']; QuotePages = utilities.hyphen_to_dash (A['QuotePages']); end local Edition = A['Edition']; local PublicationPlace = place_check (A['PublicationPlace'], A:ORIGIN('PublicationPlace')); local Place = place_check (A['Place'], A:ORIGIN('Place')); local PublisherName = A['PublisherName']; local PublisherName_origin = A:ORIGIN('PublisherName'); if utilities.is_set (PublisherName) then local i = 0; PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized if i then -- non-zero when markup was stripped so emit an error message utilities.set_message ('err_apostrophe_markup', {PublisherName_origin}); end end local Newsgroup = A['Newsgroup']; -- TODO: strip apostrophe markup? local Newsgroup_origin = A:ORIGIN('Newsgroup'); if 'newsgroup' == config.CitationClass then if utilities.is_set (PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup utilities.set_message ('err_parameter_ignored', {PublisherName_origin}); end PublisherName = nil; -- ensure that this parameter is unset for the time being; will be used again after COinS end local URL = A['URL']; -- TODO: better way to do this for URL, ChapterURL, and MapURL? local UrlAccess = is_valid_parameter_value (A['UrlAccess'], A:ORIGIN('UrlAccess'), cfg.keywords_lists['url-access'], nil); if not utilities.is_set (URL) and utilities.is_set (UrlAccess) then UrlAccess = nil; utilities.set_message ('err_param_access_requires_param', 'url'); end local ChapterURL = A['ChapterURL']; local ChapterUrlAccess = is_valid_parameter_value (A['ChapterUrlAccess'], A:ORIGIN('ChapterUrlAccess'), cfg.keywords_lists['url-access'], nil); if not utilities.is_set (ChapterURL) and utilities.is_set (ChapterUrlAccess) then ChapterUrlAccess = nil; utilities.set_message ('err_param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')}); end local MapUrlAccess = is_valid_parameter_value (A['MapUrlAccess'], A:ORIGIN('MapUrlAccess'), cfg.keywords_lists['url-access'], nil); if not utilities.is_set (A['MapURL']) and utilities.is_set (MapUrlAccess) then MapUrlAccess = nil; utilities.set_message ('err_param_access_requires_param', {'map-url'}); end local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language local no_tracking_cats = is_valid_parameter_value (A['NoTracking'], A:ORIGIN('NoTracking'), cfg.keywords_lists['yes_true_y'], nil); -- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories if not utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids? no_tracking_cats = "true"; -- set no_tracking_cats end for _, v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns if this_page.text:match (v) then -- test page name against each pattern no_tracking_cats = "true"; -- set no_tracking_cats break; -- bail out if one is found end end end -- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it) utilities.select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'err_redundant_parameters'); -- this is a dummy call simply to get the error message and category local coins_pages; Page, Pages, At, coins_pages = insource_loc_get (Page, A:ORIGIN('Page'), Pages, A:ORIGIN('Pages'), At); local NoPP = is_valid_parameter_value (A['NoPP'], A:ORIGIN('NoPP'), cfg.keywords_lists['yes_true_y'], nil); if utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- both |publication-place= and |place= (|location=) allowed if different utilities.add_prop_cat ('location-test'); -- add property cat to evaluate how often PublicationPlace and Place are used together if PublicationPlace == Place then Place = ''; -- unset; don't need both if they are the same end elseif not utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- when only |place= (|location=) is set ... PublicationPlace = Place; -- promote |place= (|location=) to |publication-place end if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL local ScriptChapter = A['ScriptChapter']; local ScriptChapter_origin = A:ORIGIN ('ScriptChapter'); local Format = A['Format']; local ChapterFormat = A['ChapterFormat']; local TransChapter = A['TransChapter']; local TransChapter_origin = A:ORIGIN ('TransChapter'); local TransTitle = A['TransTitle']; local ScriptTitle = A['ScriptTitle']; --[[ Parameter remapping for cite encyclopedia: When the citation has these parameters: |encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title= |encyclopedia= and |article= then map |encyclopedia= to |title= |trans-title= maps to |trans-chapter= when |title= is re-mapped |url= maps to |chapter-url= when |title= is remapped All other combinations of |encyclopedia=, |title=, and |article= are not modified ]] local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS if utilities.is_set (Encyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}} if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')}); Encyclopedia = nil; -- unset because not supported by this template end end if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both set emit an error TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', A:ORIGIN ('Encyclopedia')) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Periodical_origin)}); end if utilities.is_set (Encyclopedia) then Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia; allow periodical without encyclopedia Periodical_origin = A:ORIGIN ('Encyclopedia'); end if utilities.is_set (Periodical) then -- Periodical is set when |encyclopedia= is set if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then if not utilities.is_set (Chapter) then Chapter = Title; -- |encyclopedia= and |title= are set so map |title= to |article= and |encyclopedia= to |title= ScriptChapter = ScriptTitle; ScriptChapter_origin = A:ORIGIN('ScriptTitle') TransChapter = TransTitle; ChapterURL = URL; ChapterURL_origin = URL_origin; ChapterUrlAccess = UrlAccess; if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then Chapter = utilities.make_wikilink (TitleLink, Chapter); end Title = Periodical; ChapterFormat = Format; Periodical = ''; -- redundant so unset TransTitle = ''; URL = ''; Format = ''; TitleLink = ''; ScriptTitle = ''; end elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title= Periodical = ''; -- redundant so unset end end end -- special case for cite techreport. local ID = A['ID']; if (config.CitationClass == "techreport") then -- special case for cite techreport if utilities.is_set (A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue' if not utilities.is_set (ID) then -- can we use ID for the "number"? ID = A['Number']; -- yes, use it else -- ID has a value so emit error message utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'number')}); end end end -- Account for the oddity that is {{cite conference}}, before generation of COinS data. local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode local Conference = A['Conference']; local BookTitle = A['BookTitle']; local TransTitle_origin = A:ORIGIN ('TransTitle'); if 'conference' == config.CitationClass then if utilities.is_set (BookTitle) then Chapter = Title; Chapter_origin = 'title'; -- ChapterLink = TitleLink; -- |chapter-link= is deprecated ChapterURL = URL; ChapterUrlAccess = UrlAccess; ChapterURL_origin = URL_origin; URL_origin = ''; ChapterFormat = Format; TransChapter = TransTitle; TransChapter_origin = TransTitle_origin; Title = BookTitle; Format = ''; -- TitleLink = ''; TransTitle = ''; URL = ''; end elseif 'speech' ~= config.CitationClass then Conference = ''; -- not cite conference or cite speech so make sure this is empty string end -- CS1/2 mode local Mode = is_valid_parameter_value (A['Mode'], A:ORIGIN('Mode'), cfg.keywords_lists['mode'], ''); -- separator character and postscript local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass); -- controls capitalization of certain static text local use_lowercase = ( sepc == ',' ); -- cite map oddities local Cartography = ""; local Scale = ""; local Sheet = A['Sheet'] or ''; local Sheets = A['Sheets'] or ''; if config.CitationClass == "map" then if utilities.is_set (Chapter) then --TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message end Chapter = A['Map']; Chapter_origin = A:ORIGIN('Map'); ChapterURL = A['MapURL']; ChapterURL_origin = A:ORIGIN('MapURL'); TransChapter = A['TransMap']; ScriptChapter = A['ScriptMap'] ScriptChapter_origin = A:ORIGIN('ScriptMap') ChapterUrlAccess = MapUrlAccess; ChapterFormat = A['MapFormat']; Cartography = A['Cartography']; if utilities.is_set ( Cartography ) then Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase); end Scale = A['Scale']; if utilities.is_set ( Scale ) then Scale = sepc .. " " .. Scale; end end -- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data. local Series = A['Series']; if 'episode' == config.CitationClass or 'serial' == config.CitationClass then local SeriesLink = A['SeriesLink']; SeriesLink = link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series'); -- check for wiki-markup in |series-link= or wiki-markup in |series= when |series-link= is set local Network = A['Network']; local Station = A['Station']; local s, n = {}, {}; -- do common parameters first if utilities.is_set (Network) then table.insert(n, Network); end if utilities.is_set (Station) then table.insert(n, Station); end ID = table.concat(n, sepc .. ' '); if 'episode' == config.CitationClass then -- handle the oddities that are strictly {{cite episode}} local Season = A['Season']; local SeriesNumber = A['SeriesNumber']; if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message SeriesNumber = ''; -- unset; prefer |season= over |seriesno= end -- assemble a table of parts concatenated later into Series if utilities.is_set (Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end if utilities.is_set (SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end if utilities.is_set (Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end Issue = ''; -- unset because this is not a unique parameter Chapter = Title; -- promote title parameters to chapter ScriptChapter = ScriptTitle; ScriptChapter_origin = A:ORIGIN('ScriptTitle'); ChapterLink = TitleLink; -- alias |episode-link= TransChapter = TransTitle; ChapterURL = URL; ChapterUrlAccess = UrlAccess; ChapterURL_origin = URL_origin; ChapterFormat = Format; Title = Series; -- promote series to title TitleLink = SeriesLink; Series = table.concat(s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number if utilities.is_set (ChapterLink) and not utilities.is_set (ChapterURL) then -- link but not URL Chapter = utilities.make_wikilink (ChapterLink, Chapter); elseif utilities.is_set (ChapterLink) and utilities.is_set (ChapterURL) then -- if both are set, URL links episode; Series = utilities.make_wikilink (ChapterLink, Series); end URL = ''; -- unset TransTitle = ''; ScriptTitle = ''; Format = ''; else -- now oddities that are cite serial Issue = ''; -- unset because this parameter no longer supported by the citation/core version of cite serial Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday? if utilities.is_set (Series) and utilities.is_set (SeriesLink) then Series = utilities.make_wikilink (SeriesLink, Series); end Series = utilities.wrap_style ('italic-title', Series); -- series is italicized end end -- end of {{cite episode}} stuff -- handle type parameter for those CS1 citations that have default values local TitleType = A['TitleType']; local Degree = A['Degree']; if utilities.in_array (config.CitationClass, {'AV-media-notes', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then TitleType = set_titletype (config.CitationClass, TitleType); if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower(); end end if utilities.is_set (TitleType) then -- if type parameter is specified TitleType = utilities.substitute ( cfg.messages['type'], TitleType); -- display it in parentheses -- TODO: Hack on TitleType to fix bunched parentheses problem end -- legacy: promote PublicationDate to Date if neither Date nor Year are set. local Date = A['Date']; local Date_origin; -- to hold the name of parameter promoted to Date; required for date error messaging local PublicationDate = A['PublicationDate']; local Year = A['Year']; if not utilities.is_set (Date) then Date = Year; -- promote Year to Date Year = nil; -- make nil so Year as empty string isn't used for CITEREF if not utilities.is_set (Date) and utilities.is_set (PublicationDate) then -- use PublicationDate when |date= and |year= are not set Date = PublicationDate; -- promote PublicationDate to Date PublicationDate = ''; -- unset, no longer needed Date_origin = A:ORIGIN('PublicationDate'); -- save the name of the promoted parameter else Date_origin = A:ORIGIN('Year'); -- save the name of the promoted parameter end else Date_origin = A:ORIGIN('Date'); -- not a promotion; name required for error messaging end if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation --[[ Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where we get the date used in the metadata. Date validation supporting code is in Module:Citation/CS1/Date_validation ]] local DF = is_valid_parameter_value (A['DF'], A:ORIGIN('DF'), cfg.keywords_lists['df'], ''); if not utilities.is_set (DF) then DF = cfg.global_df; -- local |df= if present overrides global df set by {{use xxx date}} template end local ArchiveURL; local ArchiveDate; local ArchiveFormat = A['ArchiveFormat']; ArchiveURL, ArchiveDate = archive_url_check (A['ArchiveURL'], A['ArchiveDate']) ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url'); ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL local AccessDate = A['AccessDate']; local LayDate = A['LayDate']; local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification local DoiBroken = A['DoiBroken']; local Embargo = A['Embargo']; local anchor_year; -- used in the CITEREF identifier do -- create defined block to contain local variables error_message, date_parameters_list, mismatch local error_message = ''; -- AirDate has been promoted to Date so not necessary to check it local date_parameters_list = { ['access-date'] = {val = AccessDate, name = A:ORIGIN ('AccessDate')}, ['archive-date'] = {val = ArchiveDate, name = A:ORIGIN ('ArchiveDate')}, ['date'] = {val = Date, name = Date_origin}, ['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')}, ['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')}, ['lay-date'] = {val = LayDate, name = A:ORIGIN ('LayDate')}, ['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')}, ['year'] = {val = Year, name = A:ORIGIN ('Year')}, }; local error_list = {}; anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list); -- start temporary Julian / Gregorian calendar uncertainty categorization if COinS_date.inter_cal_cat then utilities.add_prop_cat ('jul-greg-uncertainty'); end -- end temporary Julian / Gregorian calendar uncertainty categorization if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed; validation.year_date_check (Year, A:ORIGIN ('Year'), Date, A:ORIGIN ('Date'), error_list); end if 0 == #error_list then -- error free dates only; 0 when error_list is empty local modified = false; -- flag if utilities.is_set (DF) then -- if we need to reformat dates modified = validation.reformat_dates (date_parameters_list, DF); -- reformat to DF format, use long month names if appropriate end if true == validation.date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate modified = true; utilities.set_message ('maint_date_format'); -- hyphens were converted so add maint category end -- for those wikis that can and want to have English date names translated to the local language; not supported at en.wiki if cfg.date_name_auto_xlate_enable and validation.date_name_xlate (date_parameters_list, cfg.date_digit_auto_xlate_enable ) then utilities.set_message ('maint_date_auto_xlated'); -- add maint cat modified = true; end if modified then -- if the date_parameters_list values were modified AccessDate = date_parameters_list['access-date'].val; -- overwrite date holding parameters with modified values ArchiveDate = date_parameters_list['archive-date'].val; Date = date_parameters_list['date'].val; DoiBroken = date_parameters_list['doi-broken-date'].val; LayDate = date_parameters_list['lay-date'].val; PublicationDate = date_parameters_list['publication-date'].val; end else utilities.set_message ('err_bad_date', {utilities.make_sep_list (#error_list, error_list)}); -- add this error message end end -- end of do local ID_list = {}; -- sequence table of rendered identifiers local ID_list_coins = {}; -- table of identifiers and their values from args; key is same as cfg.id_handlers's key local Class = A['Class']; -- arxiv class identifier local ID_support = { {A['ASINTLD'], 'ASIN', 'err_asintld_missing_asin', A:ORIGIN ('ASINTLD')}, {DoiBroken, 'DOI', 'err_doibroken_missing_doi', A:ORIGIN ('DoiBroken')}, {Embargo, 'PMC', 'err_embargo_missing_pmc', A:ORIGIN ('Embargo')}, } ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class}, ID_support); -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite ssrn}}, before generation of COinS data. if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv=, |citeseerx=, |ssrn= required for their templates if not (args[cfg.id_handlers[config.CitationClass:upper()].parameters[1]] or -- can't use ID_list_coins k/v table here because invalid parameters omitted args[cfg.id_handlers[config.CitationClass:upper()].parameters[2]]) then -- which causes unexpected parameter missing error message utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message end Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass]; end -- Link the title of the work if no |url= was provided, but we have a |pmc= or a |doi= with |doi-access=free if config.CitationClass == "journal" and not utilities.is_set (URL) and not utilities.is_set (TitleLink) and not utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) then -- TODO: remove 'none' once existing citations have been switched to 'off', so 'none' can be used as token for "no title" instead if 'none' ~= cfg.keywords_xlate[auto_select] then -- if auto-linking not disabled if identifiers.auto_link_urls[auto_select] then -- manual selection URL = identifiers.auto_link_urls[auto_select]; -- set URL to be the same as identifier's external link URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title= elseif identifiers.auto_link_urls['pmc'] then -- auto-select PMC URL = identifiers.auto_link_urls['pmc']; -- set URL to be the same as the PMC external link if not embargoed URL_origin = cfg.id_handlers['PMC'].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title= elseif identifiers.auto_link_urls['doi'] then -- auto-select DOI URL = identifiers.auto_link_urls['doi']; URL_origin = cfg.id_handlers['DOI'].parameters[1]; end end if utilities.is_set (URL) then -- set when using an identifier-created URL if utilities.is_set (AccessDate) then -- |access-date= requires |url=; identifier-created URL is not |url= utilities.set_message ('err_accessdate_missing_url'); -- add an error message AccessDate = ''; -- unset end if utilities.is_set (ArchiveURL) then -- |archive-url= requires |url=; identifier-created URL is not |url= utilities.set_message ('err_archive_missing_url'); -- add an error message ArchiveURL = ''; -- unset end end end -- At this point fields may be nil if they weren't specified in the template use. We can use that fact. -- Test if citation has no title if not utilities.is_set (Title) and not utilities.is_set (TransTitle) and not utilities.is_set (ScriptTitle) then -- has special case for cite episode utilities.set_message ('err_citation_missing_title', {'episode' == config.CitationClass and 'series' or 'title'}); end if utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) and utilities.in_array (config.CitationClass, {'journal', 'citation'}) and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and ('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then -- special case for journal cites Title = ''; -- set title to empty string utilities.set_message ('maint_untitled'); -- add maint cat end -- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information. -- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that -- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title -- is the article title, and Chapter is a section within the article. So, we remap local coins_chapter = Chapter; -- default assuming that remapping not required local coins_title = Title; -- et tu if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then if utilities.is_set (Chapter) and utilities.is_set (Title) and utilities.is_set (Periodical) then -- if all are used then coins_chapter = Title; -- remap coins_title = Periodical; end end local coins_author = a; -- default for coins rft.au if 0 < #c then -- but if contributor list coins_author = c; -- use that instead end -- this is the function call to COinS() local OCinSoutput = metadata.COinS({ ['Periodical'] = utilities.strip_apostrophe_markup (Periodical), -- no markup in the metadata ['Encyclopedia'] = Encyclopedia, -- just a flag; content ignored by ~/COinS ['Chapter'] = metadata.make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic / accept-as-written markup ['Degree'] = Degree; -- cite thesis only ['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup ['PublicationPlace'] = PublicationPlace, ['Date'] = COinS_date.rftdate, -- COinS_date has correctly formatted date if Date is valid; ['Season'] = COinS_date.rftssn, ['Quarter'] = COinS_date.rftquarter, ['Chron'] = COinS_date.rftchron or (not COinS_date.rftdate and Date) or '', -- chron but if not set and invalid date format use Date; keep this last bit? ['Series'] = Series, ['Volume'] = Volume, ['Issue'] = Issue, ['ArticleNumber'] = ArticleNumber, ['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links ['Edition'] = Edition, ['PublisherName'] = PublisherName or Newsgroup, -- any apostrophe markup already removed from PublisherName ['URL'] = first_set ({ChapterURL, URL}, 2), ['Authors'] = coins_author, ['ID_list'] = ID_list_coins, ['RawPage'] = this_page.prefixedText, }, config.CitationClass); -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, and {{cite ssrn}} AFTER generation of COinS data. if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, or ssrn now unset so it isn't displayed Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal end -- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text if 'newsgroup' == config.CitationClass and utilities.is_set (Newsgroup) then PublisherName = utilities.substitute (cfg.messages['newsgroup'], external_link( 'news:' .. Newsgroup, Newsgroup, Newsgroup_origin, nil )); end local Editors; local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list local Contributors; -- assembled contributors name list local contributor_etal; local Translators; -- assembled translators name list local translator_etal; local t = {}; -- translators list from |translator-lastn= / translator-firstn= pairs t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn= local Interviewers; local interviewers_list = {}; interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters local interviewer_etal; -- Now perform various field substitutions. -- We also add leading spaces and surrounding markup and punctuation to the -- various parts of the citation, but only when they are non-nil. do local last_first_list; local control = { format = NameListStyle, -- empty string or 'vanc' maximum = nil, -- as if display-authors or display-editors not set mode = Mode }; do -- do editor name list first because the now unsupported coauthors used to modify control table control.maximum , editor_etal = get_display_names (A['DisplayEditors'], #e, 'editors', editor_etal, A:ORIGIN ('DisplayEditors')); Editors, EditorCount = list_people (control, e, editor_etal); if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then EditorCount = 2; -- spoof to display (eds.) annotation end end do -- now do interviewers control.maximum, interviewer_etal = get_display_names (A['DisplayInterviewers'], #interviewers_list, 'interviewers', interviewer_etal, A:ORIGIN ('DisplayInterviewers')); Interviewers = list_people (control, interviewers_list, interviewer_etal); end do -- now do translators control.maximum, translator_etal = get_display_names (A['DisplayTranslators'], #t, 'translators', translator_etal, A:ORIGIN ('DisplayTranslators')); Translators = list_people (control, t, translator_etal); end do -- now do contributors control.maximum, contributor_etal = get_display_names (A['DisplayContributors'], #c, 'contributors', contributor_etal, A:ORIGIN ('DisplayContributors')); Contributors = list_people (control, c, contributor_etal); end do -- now do authors control.maximum, author_etal = get_display_names (A['DisplayAuthors'], #a, 'authors', author_etal, A:ORIGIN ('DisplayAuthors')); last_first_list = list_people (control, a, author_etal); if utilities.is_set (Authors) then Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors'); -- find and remove variations on et al. if author_etal then Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter end else Authors = last_first_list; -- either an author name list or an empty string end end -- end of do if utilities.is_set (Authors) and utilities.is_set (Collaboration) then Authors = Authors .. ' (' .. Collaboration .. ')'; -- add collaboration after et al. end end local ConferenceFormat = A['ConferenceFormat']; local ConferenceURL = A['ConferenceURL']; ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url'); Format = style_format (Format, URL, 'format', 'url'); -- special case for chapter format so no error message or cat when chapter not supported if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or ('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url'); end if not utilities.is_set (URL) then if utilities.in_array (config.CitationClass, {"web", "podcast", "mailinglist"}) or -- |url= required for cite web, cite podcast, and cite mailinglist ('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then -- and required for {{citation}} with |website= or |script-website= utilities.set_message ('err_cite_web_url'); end -- do we have |accessdate= without either |url= or |chapter-url=? if utilities.is_set (AccessDate) and not utilities.is_set (ChapterURL) then -- ChapterURL may be set when URL is not set; utilities.set_message ('err_accessdate_missing_url'); AccessDate = ''; end end local UrlStatus = is_valid_parameter_value (A['UrlStatus'], A:ORIGIN('UrlStatus'), cfg.keywords_lists['url-status'], ''); local OriginalURL local OriginalURL_origin local OriginalFormat local OriginalAccess; UrlStatus = UrlStatus:lower(); -- used later when assembling archived text if utilities.is_set ( ArchiveURL ) then if utilities.is_set (ChapterURL) then -- if chapter-url= is set apply archive url to it OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text OriginalURL_origin = ChapterURL_origin; -- name of |chapter-url= parameter for error messages OriginalFormat = ChapterFormat; -- and original |chapter-format= if 'live' ~= UrlStatus then ChapterURL = ArchiveURL -- swap-in the archive's URL ChapterURL_origin = A:ORIGIN('ArchiveURL') -- name of |archive-url= parameter for error messages ChapterFormat = ArchiveFormat or ''; -- swap in archive's format ChapterUrlAccess = nil; -- restricted access levels do not make sense for archived URLs end elseif utilities.is_set (URL) then OriginalURL = URL; -- save copy of original source URL OriginalURL_origin = URL_origin; -- name of URL parameter for error messages OriginalFormat = Format; -- and original |format= OriginalAccess = UrlAccess; if 'live' ~= UrlStatus then -- if URL set then |archive-url= applies to it URL = ArchiveURL -- swap-in the archive's URL URL_origin = A:ORIGIN('ArchiveURL') -- name of archive URL parameter for error messages Format = ArchiveFormat or ''; -- swap in archive's format UrlAccess = nil; -- restricted access levels do not make sense for archived URLs end end elseif utilities.is_set (UrlStatus) then -- if |url-status= is set when |archive-url= is not set utilities.set_message ('maint_url_status'); -- add maint cat end if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia ('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then local chap_param; if utilities.is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters chap_param = A:ORIGIN ('Chapter') elseif utilities.is_set (TransChapter) then chap_param = A:ORIGIN ('TransChapter') elseif utilities.is_set (ChapterURL) then chap_param = A:ORIGIN ('ChapterURL') elseif utilities.is_set (ScriptChapter) then chap_param = ScriptChapter_origin; else utilities.is_set (ChapterFormat) chap_param = A:ORIGIN ('ChapterFormat') end if utilities.is_set (chap_param) then -- if we found one utilities.set_message ('err_chapter_ignored', {chap_param}); -- add error message Chapter = ''; -- and set them to empty string to be safe with concatenation TransChapter = ''; ChapterURL = ''; ScriptChapter = ''; ChapterFormat = ''; end else -- otherwise, format chapter / article title local no_quotes = false; -- default assume that we will be quoting the chapter parameter value if utilities.is_set (Contribution) and 0 < #c then -- if this is a contribution with contributor(s) if utilities.in_array (Contribution:lower(), cfg.keywords_lists.contribution) then -- and a generic contribution title no_quotes = true; -- then render it unquoted end end Chapter = format_chapter_title (ScriptChapter, ScriptChapter_origin, Chapter, Chapter_origin, TransChapter, TransChapter_origin, ChapterURL, ChapterURL_origin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter if utilities.is_set (Chapter) then Chapter = Chapter .. ChapterFormat ; if 'map' == config.CitationClass and utilities.is_set (TitleType) then Chapter = Chapter .. ' ' .. TitleType; -- map annotation here; not after title end Chapter = Chapter .. sepc .. ' '; elseif utilities.is_set (ChapterFormat) then -- |chapter= not set but |chapter-format= is so ... Chapter = ChapterFormat .. sepc .. ' '; -- ... ChapterFormat has error message, we want to see it end end -- Format main title local plain_title = false; local accept_title; Title, accept_title = utilities.has_accept_as_written (Title, true); -- remove accept-this-as-written markup when it wraps all of <Title> if accept_title and ('' == Title) then -- only support forced empty for now "(())" Title = cfg.messages['notitle']; -- replace by predefined "No title" message -- TODO: utilities.set_message ( 'err_redundant_parameters', ...); -- issue proper error message instead of muting ScriptTitle = ''; -- just mute for now TransTitle = ''; -- just mute for now plain_title = true; -- suppress text decoration for descriptive title utilities.set_message ('maint_untitled'); -- add maint cat end if not accept_title then -- <Title> not wrapped in accept-as-written markup if '...' == Title:sub (-3) then -- if ellipsis is the last three characters of |title= Title = Title:gsub ('(%.%.%.)%.+$', '%1'); -- limit the number of dots to three elseif not mw.ustring.find (Title, '%.%s*%a%.$') and -- end of title is not a 'dot-(optional space-)letter-dot' initialism ... not mw.ustring.find (Title, '%s+%a%.$') then -- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.) Title = mw.ustring.gsub(Title, '%' .. sepc .. '$', ''); -- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters end if utilities.is_set (ArchiveURL) and is_archived_copy (Title) then utilities.set_message ('maint_archived_copy'); -- add maintenance category before we modify the content of Title end if is_generic ('generic_titles', Title) then utilities.set_message ('err_generic_title'); -- set an error message end end if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or ('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or ('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from module provided quote marks Title = utilities.wrap_style ('quoted-title', Title); Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); elseif plain_title or ('report' == config.CitationClass) then -- no styling for cite report and descriptive titles (otherwise same as above) Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title else Title = utilities.wrap_style ('italic-title', Title); Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped TransTitle = utilities.wrap_style ('trans-italic-title', TransTitle); end if utilities.is_set (TransTitle) then if utilities.is_set (Title) then TransTitle = " " .. TransTitle; else utilities.set_message ('err_trans_missing_title', {'title'}); end end if utilities.is_set (Title) then -- TODO: is this the right place to be making Wikisource URLs? if utilities.is_set (TitleLink) and utilities.is_set (URL) then utilities.set_message ('err_wikilink_in_url'); -- set an error message because we can't have both TitleLink = ''; -- unset end if not utilities.is_set (TitleLink) and utilities.is_set (URL) then Title = external_link (URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format; URL = ''; -- unset these because no longer needed Format = ""; elseif utilities.is_set (TitleLink) and not utilities.is_set (URL) then local ws_url; ws_url = wikisource_url_make (TitleLink); -- ignore ws_label return; not used here if ws_url then Title = external_link (ws_url, Title .. '&nbsp;', 'ws link in title-link'); -- space char after Title to move icon away from italic text; TODO: a better way to do this? Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title}); Title = Title .. TransTitle; else Title = utilities.make_wikilink (TitleLink, Title) .. TransTitle; end else local ws_url, ws_label, L; -- Title has italic or quote markup by the time we get here which causes is_wikilink() to return 0 (not a wikilink) ws_url, ws_label, L = wikisource_url_make (Title:gsub('^[\'"]*(.-)[\'"]*$', '%1')); -- make ws URL from |title= interwiki link (strip italic or quote markup); link portion L becomes tooltip label if ws_url then Title = Title:gsub ('%b[]', ws_label); -- replace interwiki link with ws_label to retain markup Title = external_link (ws_url, Title .. '&nbsp;', 'ws link in title'); -- space char after Title to move icon away from italic text; TODO: a better way to do this? Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title}); Title = Title .. TransTitle; else Title = Title .. TransTitle; end end else Title = TransTitle; end if utilities.is_set (Place) then Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " "; end local ConferenceURL_origin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL if utilities.is_set (Conference) then if utilities.is_set (ConferenceURL) then Conference = external_link( ConferenceURL, Conference, ConferenceURL_origin, nil ); end Conference = sepc .. " " .. Conference .. ConferenceFormat; elseif utilities.is_set (ConferenceURL) then Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURL_origin, nil ); end local Position = ''; if not utilities.is_set (Position) then local Minutes = A['Minutes']; local Time = A['Time']; if utilities.is_set (Minutes) then if utilities.is_set (Time) then --TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'time')}); end Position = " " .. Minutes .. " " .. cfg.messages['minutes']; else if utilities.is_set (Time) then local TimeCaption = A['TimeCaption'] if not utilities.is_set (TimeCaption) then TimeCaption = cfg.messages['event']; if sepc ~= '.' then TimeCaption = TimeCaption:lower(); end end Position = " " .. TimeCaption .. " " .. Time; end end else Position = " " .. Position; At = ''; end Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase); At = utilities.is_set (At) and (sepc .. " " .. At) or ""; Position = utilities.is_set (Position) and (sepc .. " " .. Position) or ""; if config.CitationClass == 'map' then local Sections = A['Sections']; -- Section (singular) is an alias of Chapter so set earlier local Inset = A['Inset']; if utilities.is_set ( Inset ) then Inset = sepc .. " " .. wrap_msg ('inset', Inset, use_lowercase); end if utilities.is_set ( Sections ) then Section = sepc .. " " .. wrap_msg ('sections', Sections, use_lowercase); elseif utilities.is_set ( Section ) then Section = sepc .. " " .. wrap_msg ('section', Section, use_lowercase); end At = At .. Inset .. Section; end local Others = A['Others']; if utilities.is_set (Others) and 0 == #a and 0 == #e then -- add maint cat when |others= has value and used without |author=, |editor= if config.CitationClass == "AV-media-notes" or config.CitationClass == "audio-visual" then -- special maint for AV/M which has a lot of 'false' positives right now utilities.set_message ('maint_others_avm') else utilities.set_message ('maint_others'); end end Others = utilities.is_set (Others) and (sepc .. " " .. Others) or ""; if utilities.is_set (Translators) then Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc); end if utilities.is_set (Interviewers) then Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc); end local TitleNote = A['TitleNote']; TitleNote = utilities.is_set (TitleNote) and (sepc .. " " .. TitleNote) or ""; if utilities.is_set (Edition) then if Edition:match ('%f[%a][Ee]d%n?%.?$') or Edition:match ('%f[%a][Ee]dition$') then -- Ed, ed, Ed., ed., Edn, edn, Edn., edn. utilities.set_message ('err_extra_text_edition'); -- add error message end Edition = " " .. wrap_msg ('edition', Edition); else Edition = ''; end Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum local Agency = A['Agency']; Agency = utilities.is_set (Agency) and wrap_msg ('agency', {sepc, Agency}) or ""; Volume = format_volume_issue (Volume, Issue, ArticleNumber, config.CitationClass, Periodical_origin, sepc, use_lowercase); if utilities.is_set (AccessDate) then local retrv_text = " " .. cfg.messages['retrieved'] AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if mode is cs2, lower case AccessDate = utilities.substitute (retrv_text, AccessDate); -- add retrieved text AccessDate = utilities.substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates end if utilities.is_set (ID) then ID = sepc .. " " .. ID; end local Docket = A['Docket']; if "thesis" == config.CitationClass and utilities.is_set (Docket) then ID = sepc .. " Docket " .. Docket .. ID; end if "report" == config.CitationClass and utilities.is_set (Docket) then -- for cite report when |docket= is set ID = sepc .. ' ' .. Docket; -- overwrite ID even if |id= is set end if utilities.is_set (URL) then URL = " " .. external_link( URL, nil, URL_origin, UrlAccess ); end local Quote = A['Quote']; local TransQuote = A['TransQuote']; local ScriptQuote = A['ScriptQuote']; if utilities.is_set (Quote) or utilities.is_set (TransQuote) or utilities.is_set (ScriptQuote) then if utilities.is_set (Quote) then if Quote:sub(1, 1) == '"' and Quote:sub(-1, -1) == '"' then -- if first and last characters of quote are quote marks Quote = Quote:sub(2, -2); -- strip them off end end Quote = kern_quotes (Quote); -- kern if needed Quote = utilities.wrap_style ('quoted-text', Quote ); -- wrap in <q>...</q> tags if utilities.is_set (ScriptQuote) then Quote = script_concatenate (Quote, ScriptQuote, 'script-quote'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after quote is wrapped end if utilities.is_set (TransQuote) then if TransQuote:sub(1, 1) == '"' and TransQuote:sub(-1, -1) == '"' then -- if first and last characters of |trans-quote are quote marks TransQuote = TransQuote:sub(2, -2); -- strip them off end Quote = Quote .. " " .. utilities.wrap_style ('trans-quoted-title', TransQuote ); end if utilities.is_set (QuotePage) or utilities.is_set (QuotePages) then -- add page prefix local quote_prefix = ''; if utilities.is_set (QuotePage) then extra_text_in_page_check (QuotePage, 'quote-page'); -- add to maint cat if |quote-page= value begins with what looks like p., pp., etc. if not NoPP then quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, QuotePage}), '', '', ''; else quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, QuotePage}), '', '', ''; end elseif utilities.is_set (QuotePages) then extra_text_in_page_check (QuotePages, 'quote-pages'); -- add to maint cat if |quote-pages= value begins with what looks like p., pp., etc. if tonumber(QuotePages) ~= nil and not NoPP then -- if only digits, assume single page quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, QuotePages}), '', ''; elseif not NoPP then quote_prefix = utilities.substitute (cfg.messages['pp-prefix'], {sepc, QuotePages}), '', ''; else quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, QuotePages}), '', ''; end end Quote = quote_prefix .. ": " .. Quote; else Quote = sepc .. " " .. Quote; end PostScript = ""; -- cs1|2 does not supply terminal punctuation when |quote= is set end -- We check length of PostScript here because it will have been nuked by -- the quote parameters. We'd otherwise emit a message even if there wasn't -- a displayed postscript. -- TODO: Should the max size (1) be configurable? -- TODO: Should we check a specific pattern? if utilities.is_set(PostScript) and mw.ustring.len(PostScript) > 1 then utilities.set_message ('maint_postscript') end local Archived; if utilities.is_set (ArchiveURL) then local arch_text; if not utilities.is_set (ArchiveDate) then utilities.set_message ('err_archive_missing_date'); ArchiveDate = ''; -- empty string for concatenation end if "live" == UrlStatus then arch_text = cfg.messages['archived']; if sepc ~= "." then arch_text = arch_text:lower() end if utilities.is_set (ArchiveDate) then Archived = sepc .. ' ' .. utilities.substitute ( cfg.messages['archived-live'], {external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil) .. ArchiveFormat, ArchiveDate } ); else Archived = ''; end if not utilities.is_set (OriginalURL) then utilities.set_message ('err_archive_missing_url'); Archived = ''; -- empty string for concatenation end elseif utilities.is_set (OriginalURL) then -- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown' if utilities.in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then arch_text = cfg.messages['archived-unfit']; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. ' ' .. arch_text .. ArchiveDate; -- format already styled if 'bot: unknown' == UrlStatus then utilities.set_message ('maint_bot_unknown'); -- and add a category if not already added else utilities.set_message ('maint_unfit'); -- and add a category if not already added end else -- UrlStatus is empty, 'dead' arch_text = cfg.messages['archived-dead']; if sepc ~= "." then arch_text = arch_text:lower() end if utilities.is_set (ArchiveDate) then Archived = sepc .. " " .. utilities.substitute ( arch_text, { external_link( OriginalURL, cfg.messages['original'], OriginalURL_origin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled else Archived = ''; -- unset for concatenation end end else -- OriginalUrl not set arch_text = cfg.messages['archived-missing']; if sepc ~= "." then arch_text = arch_text:lower() end utilities.set_message ('err_archive_missing_url'); Archived = ''; -- empty string for concatenation end elseif utilities.is_set (ArchiveFormat) then Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message else Archived = ''; end local Lay = ''; local LaySource = A['LaySource']; local LayURL = A['LayURL']; local LayFormat = A['LayFormat']; LayFormat = style_format (LayFormat, LayURL, 'lay-format', 'lay-url'); if utilities.is_set (LayURL) then if utilities.is_set (LayDate) then LayDate = " (" .. LayDate .. ")" end if utilities.is_set (LaySource) then LaySource = " &ndash; ''" .. utilities.safe_for_italics (LaySource) .. "''"; else LaySource = ""; end if sepc == '.' then Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate else Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate end elseif utilities.is_set (LayFormat) then -- Test if |lay-format= is given without giving a |lay-url= Lay = sepc .. LayFormat; -- if set and LayURL not set, then LayFormat has error message end local TranscriptURL = A['TranscriptURL'] local TranscriptFormat = A['TranscriptFormat']; TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl'); local Transcript = A['Transcript']; local TranscriptURL_origin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL if utilities.is_set (Transcript) then if utilities.is_set (TranscriptURL) then Transcript = external_link( TranscriptURL, Transcript, TranscriptURL_origin, nil ); end Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat; elseif utilities.is_set (TranscriptURL) then Transcript = external_link( TranscriptURL, nil, TranscriptURL_origin, nil ); end local Publisher; if utilities.is_set (PublicationDate) then PublicationDate = wrap_msg ('published', PublicationDate); end if utilities.is_set (PublisherName) then if utilities.is_set (PublicationPlace) then Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate; else Publisher = sepc .. " " .. PublisherName .. PublicationDate; end elseif utilities.is_set (PublicationPlace) then Publisher= sepc .. " " .. PublicationPlace .. PublicationDate; else Publisher = PublicationDate; end local TransPeriodical = A['TransPeriodical']; local TransPeriodical_origin = A:ORIGIN ('TransPeriodical'); -- Several of the above rely upon detecting this as nil, so do it last. if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then if utilities.is_set (Title) or utilities.is_set (TitleNote) then Periodical = sepc .. " " .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin); else Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin); end end local Language = A['Language']; if utilities.is_set (Language) then Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc. else Language=''; -- language not specified so make sure this is an empty string; --[[ TODO: need to extract the wrap_msg from language_parameter so that we can solve parentheses bunching problem with Format/Language/TitleType ]] end --[[ Handle the oddity that is cite speech. This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided). ]] if "speech" == config.CitationClass then -- cite speech only TitleNote = TitleType; -- move TitleType to TitleNote so that it renders ahead of |event= TitleType = ''; -- and unset if utilities.is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter if utilities.is_set (Conference) then -- and if |event= is set Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering end end end -- Piece all bits together at last. Here, all should be non-nil. -- We build things this way because it is more efficient in LUA -- not to keep reassigning to the same string variable over and over. local tcommon; local tcommon2; -- used for book cite when |contributor= is set if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then if not (utilities.is_set (Authors) or utilities.is_set (Editors)) then Others = Others:gsub ('^' .. sepc .. ' ', ''); -- when no authors and no editors, strip leading sepc and space end if utilities.is_set (Others) then Others = safe_join ({Others, sepc .. " "}, sepc) end -- add terminal punctuation & space; check for dup sepc; TODO why do we need to do this here? tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc ); elseif utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites if utilities.is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc. tcommon = safe_join( {Title, TitleNote}, sepc ); -- author and other stuff will come after this and before tcommon2 tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc ); else tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc ); end elseif 'map' == config.CitationClass then -- special cases for cite map if utilities.is_set (Chapter) then -- map in a book; TitleType is part of Chapter tcommon = safe_join( {Title, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc ); elseif utilities.is_set (Periodical) then -- map in a periodical tcommon = safe_join( {Title, TitleType, Format, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc ); else -- a sheet or stand-alone map tcommon = safe_join( {Title, TitleType, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc ); end elseif 'episode' == config.CitationClass then -- special case for cite episode tcommon = safe_join( {Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc ); else -- all other CS1 templates tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc ); end if #ID_list > 0 then ID_list = safe_join( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc ); else ID_list = ID; end local Via = A['Via']; Via = utilities.is_set (Via) and wrap_msg ('via', Via) or ''; local idcommon; if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Lay, Quote }, sepc ); else idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Lay, Quote }, sepc ); end local text; local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At; local OrigDate = A['OrigDate']; OrigDate = utilities.is_set (OrigDate) and wrap_msg ('origdate', OrigDate) or ''; if utilities.is_set (Date) then if utilities.is_set (Authors) or utilities.is_set (Editors) then -- date follows authors or editors when authors not set Date = " (" .. Date .. ")" .. OrigDate .. sepc .. " "; -- in parentheses else -- neither of authors and editors set if (string.sub(tcommon, -1, -1) == sepc) then -- if the last character of tcommon is sepc Date = " " .. Date .. OrigDate; -- Date does not begin with sepc else Date = sepc .. " " .. Date .. OrigDate; -- Date begins with sepc end end end if utilities.is_set (Authors) then if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Authors termination Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space end if utilities.is_set (Editors) then local in_text = " "; local post_text = ""; if utilities.is_set (Chapter) and 0 == #c then in_text = in_text .. cfg.messages['in'] .. " " if (sepc ~= '.') then in_text = in_text:lower() -- lowercase for cs2 end end if EditorCount <= 1 then post_text = " (" .. cfg.messages['editor'] .. ")"; -- be consistent with no-author, no-date case else post_text = " (" .. cfg.messages['editors'] .. ")"; end Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space end if utilities.is_set (Contributors) then -- book cite and we're citing the intro, preface, etc. local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' '; if (sepc ~= '.') then by_text = by_text:lower() end -- lowercase for cs2 Authors = by_text .. Authors; -- author follows title so tweak it here if utilities.is_set (Editors) and utilities.is_set (Date) then -- when Editors make sure that Authors gets terminated Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space end if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Contributors termination Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space end text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc ); else text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc ); end elseif utilities.is_set (Editors) then if utilities.is_set (Date) then if EditorCount <= 1 then Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editor']; else Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editors']; end else if EditorCount <= 1 then Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " " else Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " " end end text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc ); else if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc ); else text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc ); end end if utilities.is_set (PostScript) and PostScript ~= sepc then text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc. text = text:sub(1, -sepc:len() - 1); end text = safe_join( {text, PostScript}, sepc ); -- Now enclose the whole thing in a <cite> element local options_t = {}; options_t.class = cite_class_attribute_make (config.CitationClass, Mode); local Ref = is_valid_parameter_value (A['Ref'], A:ORIGIN('Ref'), cfg.keywords_lists['ref'], nil, true); -- nil when |ref=harv; A['Ref'] else if 'none' ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ''] then local namelist_t = {}; -- holds selected contributor, author, editor name list local year = first_set ({Year, anchor_year}, 2); -- Year first for legacy citations and for YMD dates that require disambiguation if #c > 0 then -- if there is a contributor list namelist_t = c; -- select it elseif #a > 0 then -- or an author list namelist_t = a; elseif #e > 0 then -- or an editor list namelist_t = e; end local citeref_id; if #namelist_t > 0 then -- if there are names in namelist_t citeref_id = make_citeref_id (namelist_t, year); -- go make the CITEREF anchor if mw.uri.anchorEncode (citeref_id) == ((Ref and mw.uri.anchorEncode (Ref)) or '') then -- Ref may already be encoded (by {{sfnref}}) so citeref_id must be encoded before comparison utilities.set_message ('maint_ref_duplicates_default'); end else citeref_id = ''; -- unset end options_t.id = Ref or citeref_id; end if string.len (text:gsub('%b<>', '')) <= 2 then -- remove html and html-like tags; then get length of what remains; z.error_cats_t = {}; -- blank the categories list z.error_msgs_t = {}; -- blank the error messages list OCinSoutput = nil; -- blank the metadata string text = ''; -- blank the the citation utilities.set_message ('err_empty_citation'); -- set empty citation message and category end local render_t = {}; -- here we collect the final bits for concatenation into the rendered citation if utilities.is_set (options_t.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags table.insert (render_t, utilities.substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options_t.id), mw.text.nowiki(options_t.class), text})); -- when |ref= is set or when there is a namelist else table.insert (render_t, utilities.substitute (cfg.presentation['cite'], {mw.text.nowiki(options_t.class), text})); -- when |ref=none or when namelist_t empty and |ref= is missing or is empty end if OCinSoutput then -- blanked when citation is 'empty' so don't bother to add boilerplate metadata span table.insert (render_t, utilities.substitute (cfg.presentation['ocins'], OCinSoutput)); -- format and append metadata to the citation end local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass); local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]'; local msg_prefix = '<code class="cs1-code">{{' .. template_link .. '}}</code>: '; if 0 ~= #z.error_msgs_t then mw.addWarning (utilities.substitute (cfg.messages.warning_msg_e, template_link)); table.insert (render_t, ' '); -- insert a space between citation and its error messages table.sort (z.error_msgs_t); -- sort the error messages list; sorting includes wrapping <span> and <code> tags; hidden-error sorts ahead of visible-error local hidden = true; -- presume that the only error messages emited by this template are hidden for _, v in ipairs (z.error_msgs_t) do -- spin through the list of error messages if v:find ('cs1-visible-error', 1, true) then -- look for the visible error class name hidden = false; -- found one; so don't hide the error message prefix break; -- and done because no need to look further end end z.error_msgs_t[1] = table.concat ({utilities.error_comment (msg_prefix, hidden), z.error_msgs_t[1]}); -- add error message prefix to first error message to prevent extraneous punctuation table.insert (render_t, table.concat (z.error_msgs_t, '; ')); -- make a big string of error messages and add it to the rendering end if 0 ~= #z.maint_cats_t then mw.addWarning (utilities.substitute (cfg.messages.warning_msg_m, template_link)); table.sort (z.maint_cats_t); -- sort the maintenance messages list local maint_msgs_t = {}; -- here we collect all of the maint messages if 0 == #z.error_msgs_t then -- if no error messages table.insert (maint_msgs_t, msg_prefix); -- insert message prefix in maint message livery end for _, v in ipairs( z.maint_cats_t ) do -- append maintenance categories table.insert (maint_msgs_t, -- assemble new maint message and add it to the maint_msgs_t table table.concat ({v, ' (', utilities.substitute (cfg.messages[':cat wikilink'], v), ')'}) ); end table.insert (render_t, utilities.substitute (cfg.presentation['hidden-maint'], table.concat (maint_msgs_t, ' '))); -- wrap the group of maint messages with proper presentation and save end if not no_tracking_cats then for _, v in ipairs (z.error_cats_t) do -- append error categories table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); end for _, v in ipairs (z.maint_cats_t) do -- append maintenance categories table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); end for _, v in ipairs (z.prop_cats_t) do -- append properties categories table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); end end return table.concat (render_t); -- make a big string and done end --[[--------------------------< V A L I D A T E >-------------------------------------------------------------- Looks for a parameter's name in one of several whitelists. Parameters in the whitelist can have three values: true - active, supported parameters false - deprecated, supported parameters nil - unsupported parameters ]] local function validate (name, cite_class, empty) local name = tostring (name); local enum_name; -- for enumerated parameters, is name with enumerator replaced with '#' local state; local function state_test (state, name) -- local function to do testing of state values if true == state then return true; end -- valid actively supported parameter if false == state then if empty then return nil; end -- empty deprecated parameters are treated as unknowns deprecated_parameter (name); -- parameter is deprecated but still supported return true; end if 'tracked' == state then local base_name = name:gsub ('%d', ''); -- strip enumerators from parameter names that have them to get the base name utilities.add_prop_cat ('tracked-param', {base_name}, base_name); -- add a properties category; <base_name> modifies <key> return true; end return nil; end if name:find ('#') then -- # is a cs1|2 reserved character so parameters with # not permitted return nil; end if utilities.in_array (cite_class, whitelist.preprint_template_list ) then -- limited parameter sets allowed for these templates state = whitelist.limited_basic_arguments[name]; if true == state_test (state, name) then return true; end state = whitelist.preprint_arguments[cite_class][name]; -- look in the parameter-list for the template identified by cite_class if true == state_test (state, name) then return true; end -- limited enumerated parameters list enum_name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) state = whitelist.limited_numbered_arguments[enum_name]; if true == state_test (state, name) then return true; end return false; -- not supported because not found or name is set to nil end -- end limited parameter-set templates if utilities.in_array (cite_class, whitelist.unique_param_template_list) then -- experiment for template-specific parameters for templates that accept parameters from the basic argument list state = whitelist.unique_arguments[cite_class][name]; -- look in the template-specific parameter-lists for the template identified by cite_class if true == state_test (state, name) then return true; end end -- if here, fall into general validation state = whitelist.basic_arguments[name]; -- all other templates; all normal parameters allowed if true == state_test (state, name) then return true; end -- all enumerated parameters allowed enum_name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) state = whitelist.numbered_arguments[enum_name]; if true == state_test (state, name) then return true; end return false; -- not supported because not found or name is set to nil end --[=[-------------------------< I N T E R _ W I K I _ C H E C K >---------------------------------------------- check <value> for inter-language interwiki-link markup. <prefix> must be a MediaWiki-recognized language code. when these values have the form (without leading colon): [[<prefix>:link|label]] return label as plain-text [[<prefix>:link]] return <prefix>:link as plain-text return value as is else ]=] local function inter_wiki_check (parameter, value) local prefix = value:match ('%[%[(%a+):'); -- get an interwiki prefix if one exists local _; if prefix and cfg.inter_wiki_map[prefix:lower()] then -- if prefix is in the map, needs preceding colon so utilities.set_message ('err_bad_paramlink', parameter); -- emit an error message _, value, _ = utilities.is_wikilink (value); -- extract label portion from wikilink end return value; end --[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------ Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a parameter that is missing its pipe. There are two tests made: {{cite ... |title=Title access-date=2016-03-17}} -- the first parameter has a value and whitespace separates that value from the missing pipe parameter name {{cite ... |title=access-date=2016-03-17}} -- the first parameter has no value (whitespace after the first = is trimmed by MediaWiki) cs1|2 shares some parameter names with XML/HTML attributes: class=, title=, etc. To prevent false positives XML/HTML tags are removed before the search. If a missing pipe is detected, this function adds the missing pipe maintenance category. ]] local function missing_pipe_check (parameter, value) local capture; value = value:gsub ('%b<>', ''); -- remove XML/HTML tags because attributes: class=, title=, etc. capture = value:match ('%s+(%a[%w%-]+)%s*=') or value:match ('^(%a[%w%-]+)%s*='); -- find and categorize parameters with possible missing pipes if capture and validate (capture) then -- if the capture is a valid parameter name utilities.set_message ('err_missing_pipe', parameter); end end --[[--------------------------< H A S _ E X T R A N E O U S _ P U N C T >-------------------------------------- look for extraneous terminal punctuation in most parameter values; parameters listed in skip table are not checked ]] local function has_extraneous_punc (param, value) if 'number' == type (param) then return; end param = param:gsub ('%d+', '#'); -- enumerated name-list mask params allow terminal punct; normalize if cfg.punct_skip[param] then return; -- parameter name found in the skip table so done end if value:match ('[,;:]$') then utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat end if value:match ('^=') then -- sometimes an extraneous '=' character appears ... utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat end end --[[--------------------------< H A S _ E X T R A N E O U S _ U R L >------------------------------------------ look for extraneous url parameter values; parameters listed in skip table are not checked ]] local function has_extraneous_url (url_param_t) local url_error_t = {}; check_for_url (url_param_t, url_error_t); -- extraneous url check if 0 ~= #url_error_t then -- non-zero when there are errors table.sort (url_error_t); utilities.set_message ('err_param_has_ext_link', {utilities.make_sep_list (#url_error_t, url_error_t)}); -- add this error message end end --[[--------------------------< C I T A T I O N >-------------------------------------------------------------- This is used by templates such as {{cite book}} to create the actual citation text. ]] local function citation(frame) Frame = frame; -- save a copy in case we need to display an error message in preview mode local config = {}; -- table to store parameters from the module {{#invoke:}} for k, v in pairs( frame.args ) do -- get parameters from the {{#invoke}} frame config[k] = v; -- args[k] = v; -- crude debug support that allows us to render a citation from module {{#invoke:}}; skips parameter validation; TODO: keep? end -- i18n: set the name that your wiki uses to identify sandbox subpages from sandbox template invoke (or can be set here) local sandbox = ((config.SandboxPath and '' ~= config.SandboxPath) and config.SandboxPath) or '/sandbox'; -- sandbox path from {{#invoke:Citation/CS1/sandbox|citation|SandboxPath=/...}} is_sandbox = nil ~= string.find (frame:getTitle(), sandbox, 1, true); -- is this invoke the sandbox module? sandbox = is_sandbox and sandbox or ''; -- use i18n sandbox to load sandbox modules when this module is the sandox; live modules else local pframe = frame:getParent() local styles; cfg = mw.loadData ('Module:Citation/CS1/Configuration' .. sandbox); -- load sandbox versions of support modules when {{#invoke:Citation/CS1/sandbox|...}}; live modules else whitelist = mw.loadData ('Module:Citation/CS1/Whitelist' .. sandbox); utilities = require ('Module:Citation/CS1/Utilities' .. sandbox); validation = require ('Module:Citation/CS1/Date_validation' .. sandbox); identifiers = require ('Module:Citation/CS1/Identifiers' .. sandbox); metadata = require ('Module:Citation/CS1/COinS' .. sandbox); styles = 'Module:Citation/CS1' .. sandbox .. '/styles.css'; utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the selected cfg tables identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module validation.set_selected_modules (cfg, utilities); -- so that functions in Date validataion can see selected cfg tables and the selected Utilities module metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities is_preview_mode = not utilities.is_set (frame:preprocess ('{{REVISIONID}}')); local args = {}; -- table where we store all of the template's arguments local suggestions = {}; -- table where we store suggestions if we need to loadData them local error_text; -- used as a flag local capture; -- the single supported capture when matching unknown parameters using patterns local empty_unknowns = {}; -- sequence table to hold empty unknown params for error message listing for k, v in pairs( pframe.args ) do -- get parameters from the parent (template) frame v = mw.ustring.gsub (v, '^%s*(.-)%s*$', '%1'); -- trim leading/trailing whitespace; when v is only whitespace, becomes empty string if v ~= '' then if ('string' == type (k)) then k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits); -- for enumerated parameters, translate 'local' digits to Western 0-9 end if not validate( k, config.CitationClass ) then if type (k) ~= 'string' then -- exclude empty numbered parameters if v:match("%S+") ~= nil then error_text = utilities.set_message ('err_text_ignored', {v}); end elseif validate (k:lower(), config.CitationClass) then error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, k:lower()}); -- suggest the lowercase version of the parameter else if nil == suggestions.suggestions then -- if this table is nil then we need to load it suggestions = mw.loadData ('Module:Citation/CS1/Suggestions' .. sandbox); --load sandbox version of suggestion module when {{#invoke:Citation/CS1/sandbox|...}}; live module else end for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter capture = k:match (pattern); -- the whole match if no capture in pattern else the capture if a match if capture then -- if the pattern matches param = utilities.substitute (param, capture); -- add the capture to the suggested parameter (typically the enumerator) if validate (param, config.CitationClass) then -- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists) error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, param}); -- set the suggestion error message else error_text = utilities.set_message ('err_parameter_ignored', {k}); -- suggested param not supported by this template v = ''; -- unset end end end if not utilities.is_set (error_text) then -- couldn't match with a pattern, is there an explicit suggestion? if (suggestions.suggestions[ k:lower() ] ~= nil) and validate (suggestions.suggestions[ k:lower() ], config.CitationClass) then utilities.set_message ('err_parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower() ]}); else utilities.set_message ('err_parameter_ignored', {k}); v = ''; -- unset value assigned to unrecognized parameters (this for the limited parameter lists) end end end end args[k] = v; -- save this parameter and its value elseif not utilities.is_set (v) then -- for empty parameters if not validate (k, config.CitationClass, true) then -- is this empty parameter a valid parameter k = ('' == k) and '(empty string)' or k; -- when k is empty string (or was space(s) trimmed to empty string), replace with descriptive text table.insert (empty_unknowns, utilities.wrap_style ('parameter', k)); -- format for error message and add to the list end -- crude debug support that allows us to render a citation from module {{#invoke:}} TODO: keep? -- elseif args[k] ~= nil or (k == 'postscript') then -- when args[k] has a value from {{#invoke}} frame (we don't normally do that) -- args[k] = v; -- overwrite args[k] with empty string from pframe.args[k] (template frame); v is empty string here end -- not sure about the postscript bit; that gets handled in parameter validation; historical artifact? end if 0 ~= #empty_unknowns then -- create empty unknown error message utilities.set_message ('err_param_unknown_empty', { 1 == #empty_unknowns and '' or 's', utilities.make_sep_list (#empty_unknowns, empty_unknowns) }); end local url_param_t = {}; for k, v in pairs( args ) do if 'string' == type (k) then -- don't evaluate positional parameters has_invisible_chars (k, v); -- look for invisible characters end has_extraneous_punc (k, v); -- look for extraneous terminal punctuation in parameter values missing_pipe_check (k, v); -- do we think that there is a parameter that is missing a pipe? args[k] = inter_wiki_check (k, v); -- when language interwiki-linked parameter missing leading colon replace with wiki-link label if 'string' == type (k) and not cfg.url_skip[k] then -- when parameter k is not positional and not in url skip table url_param_t[k] = v; -- make a parameter/value list for extraneous url check end end has_extraneous_url (url_param_t); -- look for url in parameter values where a url does not belong return table.concat ({ frame:extensionTag ('templatestyles', '', {src=styles}), citation0( config, args) }); end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return {citation = citation}; 9bfe095ac3f64719c64a17280b76d0add203ad61 Template:Distinguish 10 316 627 2023-02-04T21:16:13Z wikipedia>Hog Farm 0 [[Wikipedia:Templates for discussion/Log/2023 February 4#Template:Distinguish]] closed as keep ([[WP:XFDC#4.0.13|XFDcloser]]) wikitext text/x-wiki {{#invoke:Distinguish|distinguish}}<noinclude><!-- splitting these lines causes {{Documentation}} template to terminate green shading when Distinguish is used in /doc pages. --> {{Documentation}} <!-- Add categories to the /doc subpage and interwikis to Wikidata, not here! --> </noinclude> f949a4cbfd6eb0ab77b832e69059a40a964b1fd8 Module:ConvertNumeric 828 507 1020 2023-02-09T01:04:17Z string2>Johnuniq 0 please don't use tricky syntax: stick to boring stuff that works Scribunto text/plain -- Module for converting between different representations of numbers. See talk page for user documentation. -- For unit tests see: [[Module:ConvertNumeric/testcases]] -- When editing, preview with: [[Module_talk:ConvertNumeric/testcases]] -- First, edit [[Module:ConvertNumeric/sandbox]], then preview with [[Module_talk:ConvertNumeric/sandbox/testcases]] require('strict') local ones_position = { [0] = 'zero', [1] = 'one', [2] = 'two', [3] = 'three', [4] = 'four', [5] = 'five', [6] = 'six', [7] = 'seven', [8] = 'eight', [9] = 'nine', [10] = 'ten', [11] = 'eleven', [12] = 'twelve', [13] = 'thirteen', [14] = 'fourteen', [15] = 'fifteen', [16] = 'sixteen', [17] = 'seventeen', [18] = 'eighteen', [19] = 'nineteen' } local ones_position_ord = { [0] = 'zeroth', [1] = 'first', [2] = 'second', [3] = 'third', [4] = 'fourth', [5] = 'fifth', [6] = 'sixth', [7] = 'seventh', [8] = 'eighth', [9] = 'ninth', [10] = 'tenth', [11] = 'eleventh', [12] = 'twelfth', [13] = 'thirteenth', [14] = 'fourteenth', [15] = 'fifteenth', [16] = 'sixteenth', [17] = 'seventeenth', [18] = 'eighteenth', [19] = 'nineteenth' } local ones_position_plural = { [0] = 'zeros', [1] = 'ones', [2] = 'twos', [3] = 'threes', [4] = 'fours', [5] = 'fives', [6] = 'sixes', [7] = 'sevens', [8] = 'eights', [9] = 'nines', [10] = 'tens', [11] = 'elevens', [12] = 'twelves', [13] = 'thirteens', [14] = 'fourteens', [15] = 'fifteens', [16] = 'sixteens', [17] = 'seventeens', [18] = 'eighteens', [19] = 'nineteens' } local tens_position = { [2] = 'twenty', [3] = 'thirty', [4] = 'forty', [5] = 'fifty', [6] = 'sixty', [7] = 'seventy', [8] = 'eighty', [9] = 'ninety' } local tens_position_ord = { [2] = 'twentieth', [3] = 'thirtieth', [4] = 'fortieth', [5] = 'fiftieth', [6] = 'sixtieth', [7] = 'seventieth', [8] = 'eightieth', [9] = 'ninetieth' } local tens_position_plural = { [2] = 'twenties', [3] = 'thirties', [4] = 'forties', [5] = 'fifties', [6] = 'sixties', [7] = 'seventies', [8] = 'eighties', [9] = 'nineties' } local groups = { [1] = 'thousand', [2] = 'million', [3] = 'billion', [4] = 'trillion', [5] = 'quadrillion', [6] = 'quintillion', [7] = 'sextillion', [8] = 'septillion', [9] = 'octillion', [10] = 'nonillion', [11] = 'decillion', [12] = 'undecillion', [13] = 'duodecillion', [14] = 'tredecillion', [15] = 'quattuordecillion', [16] = 'quindecillion', [17] = 'sexdecillion', [18] = 'septendecillion', [19] = 'octodecillion', [20] = 'novemdecillion', [21] = 'vigintillion', [22] = 'unvigintillion', [23] = 'duovigintillion', [24] = 'tresvigintillion', [25] = 'quattuorvigintillion', [26] = 'quinquavigintillion', [27] = 'sesvigintillion', [28] = 'septemvigintillion', [29] = 'octovigintillion', [30] = 'novemvigintillion', [31] = 'trigintillion', [32] = 'untrigintillion', [33] = 'duotrigintillion', [34] = 'trestrigintillion', [35] = 'quattuortrigintillion', [36] = 'quinquatrigintillion', [37] = 'sestrigintillion', [38] = 'septentrigintillion', [39] = 'octotrigintillion', [40] = 'noventrigintillion', [41] = 'quadragintillion', [51] = 'quinquagintillion', [61] = 'sexagintillion', [71] = 'septuagintillion', [81] = 'octogintillion', [91] = 'nonagintillion', [101] = 'centillion', [102] = 'uncentillion', [103] = 'duocentillion', [104] = 'trescentillion', [111] = 'decicentillion', [112] = 'undecicentillion', [121] = 'viginticentillion', [122] = 'unviginticentillion', [131] = 'trigintacentillion', [141] = 'quadragintacentillion', [151] = 'quinquagintacentillion', [161] = 'sexagintacentillion', [171] = 'septuagintacentillion', [181] = 'octogintacentillion', [191] = 'nonagintacentillion', [201] = 'ducentillion', [301] = 'trecentillion', [401] = 'quadringentillion', [501] = 'quingentillion', [601] = 'sescentillion', [701] = 'septingentillion', [801] = 'octingentillion', [901] = 'nongentillion', [1001] = 'millinillion', } local roman_numerals = { I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000 } local engord_tens_end = { ['twentieth'] = 20, ['thirtieth'] = 30, ['fortieth'] = 40, ['fiftieth'] = 50, ['sixtieth'] = 60, ['seventieth'] = 70, ['eightieth'] = 80, ['ninetieth'] = 90, } local eng_tens_cont = { ['twenty'] = 20, ['thirty'] = 30, ['forty'] = 40, ['fifty'] = 50, ['sixty'] = 60, ['seventy'] = 70, ['eighty'] = 80, ['ninety'] = 90, } -- Converts a given valid roman numeral (and some invalid roman numerals) to a number. Returns { -1, errorstring } on error. local function roman_to_numeral(roman) if type(roman) ~= "string" then return -1, "roman numeral not a string" end local rev = roman:reverse() local raising = true local last = 0 local result = 0 for i = 1, #rev do local c = rev:sub(i, i) local next = roman_numerals[c] if next == nil then return -1, "roman numeral contains illegal character " .. c end if next > last then result = result + next raising = true elseif next < last then result = result - next raising = false elseif raising then result = result + next else result = result - next end last = next end return result end -- Converts a given integer between 0 and 100 to English text (e.g. 47 -> forty-seven). local function numeral_to_english_less_100(num, ordinal, plural, zero) local terminal_ones, terminal_tens if ordinal then terminal_ones = ones_position_ord terminal_tens = tens_position_ord elseif plural then terminal_ones = ones_position_plural terminal_tens = tens_position_plural else terminal_ones = ones_position terminal_tens = tens_position end if num == 0 and zero ~= nil then return zero elseif num < 20 then return terminal_ones[num] elseif num % 10 == 0 then return terminal_tens[num / 10] else return tens_position[math.floor(num / 10)] .. '-' .. terminal_ones[num % 10] end end local function standard_suffix(ordinal, plural) if ordinal then return 'th' end if plural then return 's' end return '' end -- Converts a given integer (in string form) between 0 and 1000 to English text (e.g. 47 -> forty-seven). local function numeral_to_english_less_1000(num, use_and, ordinal, plural, zero) num = tonumber(num) if num < 100 then return numeral_to_english_less_100(num, ordinal, plural, zero) elseif num % 100 == 0 then return ones_position[num/100] .. ' hundred' .. standard_suffix(ordinal, plural) else return ones_position[math.floor(num/100)] .. ' hundred ' .. (use_and and 'and ' or '') .. numeral_to_english_less_100(num % 100, ordinal, plural, zero) end end -- Converts an ordinal in English text from 'zeroth' to 'ninety-ninth' inclusive to a number [0–99], else -1. local function english_to_ordinal(english) local eng = string.lower(english or '') local engord_lt20 = {} -- ones_position_ord{} keys & values swapped for k, v in pairs( ones_position_ord ) do engord_lt20[v] = k end if engord_lt20[eng] then return engord_lt20[eng] -- e.g. first -> 1 elseif engord_tens_end[eng] then return engord_tens_end[eng] -- e.g. ninetieth -> 90 else local tens, ones = string.match(eng, '^([a-z]+)[%s%-]+([a-z]+)$') if tens and ones then local tens_cont = eng_tens_cont[tens] local ones_end = engord_lt20[ones] if tens_cont and ones_end then return tens_cont + ones_end -- e.g. ninety-ninth -> 99 end end end return -1 -- Failed end -- Converts a number in English text from 'zero' to 'ninety-nine' inclusive to a number [0–99], else -1. local function english_to_numeral(english) local eng = string.lower(english or '') local eng_lt20 = { ['single'] = 1 } -- ones_position{} keys & values swapped for k, v in pairs( ones_position ) do eng_lt20[v] = k end if eng_lt20[eng] then return eng_lt20[eng] -- e.g. one -> 1 elseif eng_tens_cont[eng] then return eng_tens_cont[eng] -- e.g. ninety -> 90 else local tens, ones = string.match(eng, '^([a-z]+)[%s%-]+([a-z]+)$') if tens and ones then local tens_cont = eng_tens_cont[tens] local ones_end = eng_lt20[ones] if tens_cont and ones_end then return tens_cont + ones_end -- e.g. ninety-nine -> 99 end end end return -1 -- Failed end -- Converts a number expressed as a string in scientific notation to a string in standard decimal notation -- e.g. 1.23E5 -> 123000, 1.23E-5 = .0000123. Conversion is exact, no rounding is performed. local function scientific_notation_to_decimal(num) local exponent, subs = num:gsub("^%-?%d*%.?%d*%-?[Ee]([+%-]?%d+)$", "%1") if subs == 0 then return num end -- Input not in scientific notation, just return unmodified exponent = tonumber(exponent) local negative = num:find("^%-") local _, decimal_pos = num:find("%.") -- Mantissa will consist of all decimal digits with no decimal point local mantissa = num:gsub("^%-?(%d*)%.?(%d*)%-?[Ee][+%-]?%d+$", "%1%2") if negative and decimal_pos then decimal_pos = decimal_pos - 1 end if not decimal_pos then decimal_pos = #mantissa + 1 end -- Remove leading zeros unless decimal point is in first position while decimal_pos > 1 and mantissa:sub(1,1) == '0' do mantissa = mantissa:sub(2) decimal_pos = decimal_pos - 1 end -- Shift decimal point right for exponent > 0 while exponent > 0 do decimal_pos = decimal_pos + 1 exponent = exponent - 1 if decimal_pos > #mantissa + 1 then mantissa = mantissa .. '0' end -- Remove leading zeros unless decimal point is in first position while decimal_pos > 1 and mantissa:sub(1,1) == '0' do mantissa = mantissa:sub(2) decimal_pos = decimal_pos - 1 end end -- Shift decimal point left for exponent < 0 while exponent < 0 do if decimal_pos == 1 then mantissa = '0' .. mantissa else decimal_pos = decimal_pos - 1 end exponent = exponent + 1 end -- Insert decimal point in correct position and return return (negative and '-' or '') .. mantissa:sub(1, decimal_pos - 1) .. '.' .. mantissa:sub(decimal_pos) end -- Rounds a number to the nearest integer (NOT USED) local function round_num(x) if x%1 >= 0.5 then return math.ceil(x) else return math.floor(x) end end -- Rounds a number to the nearest two-word number (round = up, down, or "on" for round to nearest). -- Numbers with two digits before the decimal will be rounded to an integer as specified by round. -- Larger numbers will be rounded to a number with only one nonzero digit in front and all other digits zero. -- Negative sign is preserved and does not count towards word limit. local function round_for_english(num, round) -- If an integer with at most two digits, just return if num:find("^%-?%d?%d%.?$") then return num end local negative = num:find("^%-") if negative then -- We're rounding magnitude so flip it if round == 'up' then round = 'down' elseif round == 'down' then round = 'up' end end -- If at most two digits before decimal, round to integer and return local _, _, small_int, trailing_digits, round_digit = num:find("^%-?(%d?%d?)%.((%d)%d*)$") if small_int then if small_int == '' then small_int = '0' end if (round == 'up' and trailing_digits:find('[1-9]')) or (round == 'on' and tonumber(round_digit) >= 5) then small_int = tostring(tonumber(small_int) + 1) end return (negative and '-' or '') .. small_int end -- When rounding up, any number with > 1 nonzero digit will round up (e.g. 1000000.001 rounds up to 2000000) local nonzero_digits = 0 for digit in num:gfind("[1-9]") do nonzero_digits = nonzero_digits + 1 end num = num:gsub("%.%d*$", "") -- Remove decimal part -- Second digit used to determine which way to round lead digit local _, _, lead_digit, round_digit, round_digit_2, rest = num:find("^%-?(%d)(%d)(%d)(%d*)$") if tonumber(lead_digit .. round_digit) < 20 and (1 + #rest) % 3 == 0 then -- In English numbers < 20 are one word so put 2 digits in lead and round based on 3rd lead_digit = lead_digit .. round_digit round_digit = round_digit_2 else rest = round_digit_2 .. rest end if (round == 'up' and nonzero_digits > 1) or (round == 'on' and tonumber(round_digit) >= 5) then lead_digit = tostring(tonumber(lead_digit) + 1) end -- All digits but lead digit will turn to zero rest = rest:gsub("%d", "0") return (negative and '-' or '') .. lead_digit .. '0' .. rest end local denominators = { [2] = { 'half', plural = 'halves' }, [3] = { 'third' }, [4] = { 'quarter', us = 'fourth' }, [5] = { 'fifth' }, [6] = { 'sixth' }, [8] = { 'eighth' }, [9] = { 'ninth' }, [10] = { 'tenth' }, [16] = { 'sixteenth' }, } -- Return status, fraction where: -- status is a string: -- "finished" if there is a fraction with no whole number; -- "ok" if fraction is empty or valid; -- "unsupported" if bad fraction; -- fraction is a string giving (numerator / denominator) as English text, or is "". -- Only unsigned fractions with a very limited range of values are supported, -- except that if whole is empty, the numerator can use "-" to indicate negative. -- whole (string or nil): nil or "" if no number before the fraction -- numerator (string or nil): numerator, if any (default = 1 if a denominator is given) -- denominator (string or nil): denominator, if any -- sp_us (boolean): true if sp=us -- negative_word (string): word to use for negative sign, if whole is empty -- use_one (boolean): false: 2+1/2 → "two and a half"; true: "two and one-half" local function fraction_to_english(whole, numerator, denominator, sp_us, negative_word, use_one) if numerator or denominator then local finished = (whole == nil or whole == '') local sign = '' if numerator then if finished and numerator:sub(1, 1) == '-' then numerator = numerator:sub(2) sign = negative_word .. ' ' end else numerator = '1' end if not numerator:match('^%d+$') or not denominator or not denominator:match('^%d+$') then return 'unsupported', '' end numerator = tonumber(numerator) denominator = tonumber(denominator) local dendata = denominators[denominator] if not (dendata and 1 <= numerator and numerator <= 99) then return 'unsupported', '' end local numstr, denstr local sep = '-' if numerator == 1 then denstr = sp_us and dendata.us or dendata[1] if finished or use_one then numstr = 'one' elseif denstr:match('^[aeiou]') then numstr = 'an' sep = ' ' else numstr = 'a' sep = ' ' end else numstr = numeral_to_english_less_100(numerator) denstr = dendata.plural if not denstr then denstr = (sp_us and dendata.us or dendata[1]) .. 's' end end if finished then return 'finished', sign .. numstr .. sep .. denstr end return 'ok', ' and ' .. numstr .. sep .. denstr end return 'ok', '' end -- Takes a decimal number and converts it to English text. -- Return nil if a fraction cannot be converted (only some numbers are supported for fractions). -- num (string or nil): the number to convert. -- Can be an arbitrarily large decimal, such as "-123456789123456789.345", and -- can use scientific notation (e.g. "1.23E5"). -- May fail for very large numbers not listed in "groups" such as "1E4000". -- num is nil if there is no whole number before a fraction. -- numerator (string or nil): numerator of fraction (nil if no fraction) -- denominator (string or nil): denominator of fraction (nil if no fraction) -- capitalize (boolean): whether to capitalize the result (e.g. 'One' instead of 'one') -- use_and (boolean): whether to use the word 'and' between tens/ones place and higher places -- hyphenate (boolean): whether to hyphenate all words in the result, useful as an adjective -- ordinal (boolean): whether to produce an ordinal (e.g. 'first' instead of 'one') -- plural (boolean): whether to pluralize the resulting number -- links: nil: do not add any links; 'on': link "billion" and larger to Orders of magnitude article; -- any other text: list of numbers to link (e.g. "billion,quadrillion") -- negative_word: word to use for negative sign (typically 'negative' or 'minus'; nil to use default) -- round: nil or '': no rounding; 'on': round to nearest two-word number; 'up'/'down': round up/down to two-word number -- zero: word to use for value '0' (nil to use default) -- use_one (boolean): false: 2+1/2 → "two and a half"; true: "two and one-half" local function _numeral_to_english(num, numerator, denominator, capitalize, use_and, hyphenate, ordinal, plural, links, negative_word, round, zero, use_one) if not negative_word then if use_and then -- TODO Should 'minus' be used when do not have sp=us? -- If so, need to update testcases, and need to fix "minus zero". -- negative_word = 'minus' negative_word = 'negative' else negative_word = 'negative' end end local status, fraction_text = fraction_to_english(num, numerator, denominator, not use_and, negative_word, use_one) if status == 'unsupported' then return nil end if status == 'finished' then -- Input is a fraction with no whole number. -- Hack to avoid executing stuff that depends on num being a number. local s = fraction_text if hyphenate then s = s:gsub("%s", "-") end if capitalize then s = s:gsub("^%l", string.upper) end return s end num = scientific_notation_to_decimal(num) if round and round ~= '' then if round ~= 'on' and round ~= 'up' and round ~= 'down' then error("Invalid rounding mode") end num = round_for_english(num, round) end -- Separate into negative sign, num (digits before decimal), decimal_places (digits after decimal) local MINUS = '−' -- Unicode U+2212 MINUS SIGN (may be in values from [[Module:Convert]]) if num:sub(1, #MINUS) == MINUS then num = '-' .. num:sub(#MINUS + 1) -- replace MINUS with '-' elseif num:sub(1, 1) == '+' then num = num:sub(2) -- ignore any '+' end local negative = num:find("^%-") local decimal_places, subs = num:gsub("^%-?%d*%.(%d+)$", "%1") if subs == 0 then decimal_places = nil end num, subs = num:gsub("^%-?(%d*)%.?%d*$", "%1") if num == '' and decimal_places then num = '0' end if subs == 0 or num == '' then error("Invalid decimal numeral") end -- For each group of 3 digits except the last one, print with appropriate group name (e.g. million) local s = '' while #num > 3 do if s ~= '' then s = s .. ' ' end local group_num = math.floor((#num - 1) / 3) local group = groups[group_num] local group_digits = #num - group_num*3 s = s .. numeral_to_english_less_1000(num:sub(1, group_digits), false, false, false, zero) .. ' ' if links and (((links == 'on' and group_num >= 3) or links:find(group)) and group_num <= 13) then s = s .. '[[Orders_of_magnitude_(numbers)#10' .. group_num*3 .. '|' .. group .. ']]' else s = s .. group end num = num:sub(1 + group_digits) num = num:gsub("^0*", "") -- Trim leading zeros end -- Handle final three digits of integer part if s ~= '' and num ~= '' then if #num <= 2 and use_and then s = s .. ' and ' else s = s .. ' ' end end if s == '' or num ~= '' then s = s .. numeral_to_english_less_1000(num, use_and, ordinal, plural, zero) elseif ordinal or plural then -- Round numbers like "one million" take standard suffixes for ordinal/plural s = s .. standard_suffix(ordinal, plural) end -- For decimal places (if any) output "point" followed by spelling out digit by digit if decimal_places then s = s .. ' point' for i = 1, #decimal_places do s = s .. ' ' .. ones_position[tonumber(decimal_places:sub(i,i))] end end s = s:gsub("^%s*(.-)%s*$", "%1") -- Trim whitespace if ordinal and plural then s = s .. 's' end -- s suffix works for all ordinals if negative and s ~= zero then s = negative_word .. ' ' .. s end s = s:gsub("negative zero", "zero") s = s .. fraction_text if hyphenate then s = s:gsub("%s", "-") end if capitalize then s = s:gsub("^%l", string.upper) end return s end local function _numeral_to_english2(args) local num = tostring(args.num) num = num:gsub("^%s*(.-)%s*$", "%1") -- Trim whitespace num = num:gsub(",", "") -- Remove commas num = num:gsub("^<span[^<>]*></span>", "") -- Generated by Template:age if num ~= '' then -- a fraction may have an empty whole number if not num:find("^%-?%d*%.?%d*%-?[Ee]?[+%-]?%d*$") then -- Input not in a valid format, try to eval it as an expr to see -- if that produces a number (e.g. "3 + 5" will become "8"). local noerr, result = pcall(mw.ext.ParserFunctions.expr, num) if noerr then num = result end end end -- Call helper function passing args return _numeral_to_english( num, args['numerator'], args['denominator'], args['capitalize'], args['use_and'], args['hyphenate'], args['ordinal'], args['plural'], args['links'], args['negative_word'], args['round'], args['zero'], args['use_one'] ) or '' end local p = { -- Functions that can be called from another module roman_to_numeral = roman_to_numeral, spell_number = _numeral_to_english, spell_number2 = _numeral_to_english2, english_to_ordinal = english_to_ordinal, english_to_numeral = english_to_numeral, } function p._roman_to_numeral(frame) -- Callable via {{#invoke:ConvertNumeric|_roman_to_numeral|VI}} return roman_to_numeral(frame.args[1]) end function p._english_to_ordinal(frame) -- callable via {{#invoke:ConvertNumeric|_english_to_ordinal|First}} return english_to_ordinal(frame.args[1]) end function p._english_to_numeral(frame) -- callable via {{#invoke:ConvertNumeric|_english_to_numeral|One}} return english_to_numeral(frame.args[1]) end function p.numeral_to_english(frame) local args = frame.args -- Tail call to helper function passing args from frame return _numeral_to_english2{ ['num'] = args[1], ['numerator'] = args['numerator'], ['denominator'] = args['denominator'], ['capitalize'] = args['case'] == 'U' or args['case'] == 'u', ['use_and'] = args['sp'] ~= 'us', ['hyphenate'] = args['adj'] == 'on', ['ordinal'] = args['ord'] == 'on', ['plural'] = args['pl'] == 'on', ['links'] = args['lk'], ['negative_word'] = args['negative'], ['round'] = args['round'], ['zero'] = args['zero'], ['use_one'] = args['one'] == 'one' -- experiment: using '|one=one' makes fraction 2+1/2 give "two and one-half" instead of "two and a half" } end ---- recursive function for p.decToHex local function decToHexDigit(dec) local dig = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"} local div = math.floor(dec/16) local mod = dec-(16*div) if div >= 1 then return decToHexDigit(div)..dig[mod+1] else return dig[mod+1] end end -- I think this is supposed to be done with a tail call but first I want something that works at all ---- finds all the decimal numbers in the input text and hexes each of them function p.decToHex(frame) local args=frame.args local parent=frame.getParent(frame) local pargs={} if parent then pargs=parent.args end local text=args[1] or pargs[1] or "" local minlength=args.minlength or pargs.minlength or 1 minlength=tonumber(minlength) local prowl=mw.ustring.gmatch(text,"(.-)(%d+)") local output="" repeat local chaff,dec=prowl() if not(dec) then break end local hex=decToHexDigit(dec) while (mw.ustring.len(hex)<minlength) do hex="0"..hex end output=output..chaff..hex until false local chaff=mw.ustring.match(text,"(%D+)$") or "" return output..chaff end return p dbb876f8710ee1407b421a28b3e9bc5a6ea36cda Template:BattleTech Universe 10 475 950 2023-02-09T01:28:07Z wikipedia>Xanarki 0 add-ons wikitext text/x-wiki {{Navbox |name = BattleTech Universe |title = ''[[BattleTech]]'' |listclass = hlist | state = {{{state|}}} |group1 = Universe |list1 = * [[BattleTech (fictional setting)]] |group2 = [[List of BattleTech games|Tabletop games]] |list2 = * ''[[Classic BattleTech]]'' * ''[[CityTech]]'' * ''[[AeroTech]]'' * ''[[BattleForce]]'' * ''[[BattleSpace]]'' * ''[[BattleTech Collectible Card Game]]'' * ''[[BattleTroops]]'' * ''[[MechWarrior (role-playing game)|MechWarrior]]'' * ''[[MechWarrior: Dark Age]]'' |group3 = [[MechWarrior|''MechWarrior'' series]] |list3 = * [[MechWarrior (1989 video game)|''MechWarrior'']] ** [[MechWarrior (1993 video game)|SNES]] ** ''[[MechWarrior 3050|3050]]'' * ''[[MechWarrior 2: 31st Century Combat|2: 31st Century Combat]]'' ** ''[[MechWarrior 2: Ghost Bear's Legacy|Ghost Bear's Legacy]]'' ** ''[[MechWarrior 2: Mercenaries|Mercenaries]]'' * ''[[MechWarrior 3|3]]'' ** ''[[MechWarrior 3#Pirate's Moon (expansion pack)|Pirate's Moon]]'' * ''[[MechWarrior 4: Vengeance|4: Vengeance]]'' ** ''[[MechWarrior 4: Vengeance#Black Knight|Black Knight]]'' ** ''[[MechWarrior 4: Mercenaries|Mercenaries]]'' * ''[[MechWarrior: Living Legends|Living Legends]]'' * ''[[MechWarrior Online|Online]]'' * ''[[MechWarrior: Tactical Command| Tactical Command]]'' * ''[[MechWarrior Tactics|Tactics]]'' * ''[[MechWarrior 5: Mercenaries|5: Mercenaries]]'' ** ''[[MechWarrior 5: Mercenaries#DLC1: Heroes of The Inner Sphere & Year One Update & Xbox release|Heroes of the Inner Sphere]]'' ** ''[[MechWarrior 5: Mercenaries#DLC2: Kestrel Lancers and PlayStation release|Kestrel Lancers]]'' |group4 = Other video games |list4 = * ''[[BattleTech: The Crescent Hawk's Inception]]'' * ''[[BattleTech: The Crescent Hawk's Revenge]]'' * ''[[MechAssault]]'' ** ''[[MechAssault 2: Lone Wolf|2: Lone Wolf]]'' ** ''[[MechAssault: Phantom War|Phantom War]]'' * ''[[MechCommander]]'' ** ''[[MechCommander 2|2]]'' * ''[[Multiplayer BattleTech 3025]]'' ** ''[[Multiplayer BattleTech: EGA|EGA]]'' ** ''[[Multiplayer BattleTech: Solaris|Solaris]]'' * ''[[BattleTech (video game)|BattleTech]]'' |group5 = Other |list5 = * [[FASA]] * [[WizKids]] * [[Fantasy Productions|FanPro]] * [[Catalyst Game Labs]] * [[BattleTech Centers]] * ''[[BattleTech: The Animated Series]]'' * ''[[BattleTechnology]]'' * [[List of BattleTech novels|List of novels]] * [[MechWarrior: Dark Age (novels)|''MechWarrior: Dark Age'']] (novels) | below ='''{{Icon|Category}} [[:Category:BattleTech|Category]]''' }}<noinclude>{{documentation|content= {{collapsible option}} [[Category:Video game navigational boxes by series]] }}</noinclude> cd82bd26e7554dafb8e21a7c589123c242625b39 Module:Lua banner 828 366 726 2023-02-16T14:39:53Z wikipedia>Uzume 0 [[Module:Citation]] has been blanked since [[Wikipedia:Templates for discussion/Log/2018 May 13#Module:Citation]]; remove special handling Scribunto text/plain -- This module implements the {{lua}} template. local yesno = require('Module:Yesno') local mList = require('Module:List') local mTableTools = require('Module:TableTools') local mMessageBox = require('Module:Message box') local p = {} function p.main(frame) local origArgs = frame:getParent().args local args = {} for k, v in pairs(origArgs) do v = v:match('^%s*(.-)%s*$') if v ~= '' then args[k] = v end end return p._main(args) end function p._main(args) local modules = mTableTools.compressSparseArray(args) local box = p.renderBox(modules) local trackingCategories = p.renderTrackingCategories(args, modules) return box .. trackingCategories end function p.renderBox(modules) local boxArgs = {} if #modules < 1 then boxArgs.text = '<strong class="error">Error: no modules specified</strong>' else local moduleLinks = {} for i, module in ipairs(modules) do moduleLinks[i] = string.format('[[:%s]]', module) local maybeSandbox = mw.title.new(module .. '/sandbox') if maybeSandbox.exists then moduleLinks[i] = moduleLinks[i] .. string.format(' ([[:%s|sandbox]])', maybeSandbox.fullText) end end local moduleList = mList.makeList('bulleted', moduleLinks) local title = mw.title.getCurrentTitle() if title.subpageText == "doc" then title = title.basePageTitle end if title.contentModel == "Scribunto" then boxArgs.text = 'This module depends on the following other modules:' .. moduleList else boxArgs.text = 'This template uses [[Wikipedia:Lua|Lua]]:\n' .. moduleList end end boxArgs.type = 'notice' boxArgs.small = true boxArgs.image = '[[File:Lua-Logo.svg|30px|alt=|link=]]' return mMessageBox.main('mbox', boxArgs) end function p.renderTrackingCategories(args, modules, titleObj) if yesno(args.nocat) then return '' end local cats = {} -- Error category if #modules < 1 then cats[#cats + 1] = 'Lua templates with errors' end -- Lua templates category titleObj = titleObj or mw.title.getCurrentTitle() local subpageBlacklist = { doc = true, sandbox = true, sandbox2 = true, testcases = true } if not subpageBlacklist[titleObj.subpageText] then local protCatName if titleObj.namespace == 10 then local category = args.category if not category then local categories = { ['Module:String'] = 'Templates based on the String Lua module', ['Module:Math'] = 'Templates based on the Math Lua module', ['Module:BaseConvert'] = 'Templates based on the BaseConvert Lua module', ['Module:Citation/CS1'] = 'Templates based on the Citation/CS1 Lua module' } category = modules[1] and categories[modules[1]] category = category or 'Lua-based templates' end cats[#cats + 1] = category protCatName = "Templates using under-protected Lua modules" elseif titleObj.namespace == 828 then protCatName = "Modules depending on under-protected modules" end if not args.noprotcat and protCatName then local protLevels = { autoconfirmed = 1, extendedconfirmed = 2, templateeditor = 3, sysop = 4 } local currentProt if titleObj.id ~= 0 then -- id is 0 (page does not exist) if am previewing before creating a template. currentProt = titleObj.protectionLevels["edit"][1] end if currentProt == nil then currentProt = 0 else currentProt = protLevels[currentProt] end for i, module in ipairs(modules) do if module ~= "WP:libraryUtil" then local moduleProt = mw.title.new(module).protectionLevels["edit"][1] if moduleProt == nil then moduleProt = 0 else moduleProt = protLevels[moduleProt] end if moduleProt < currentProt then cats[#cats + 1] = protCatName break end end end end end for i, cat in ipairs(cats) do cats[i] = string.format('[[Category:%s]]', cat) end return table.concat(cats) end return p 03ec1b34a40121efc562c0c64a67ebbf57d56dff Template:Efn 10 450 900 2023-02-17T03:04:27Z wikipedia>RMCD bot 0 Removing notice of move discussion wikitext text/x-wiki <includeonly>{{#if:{{{name|}}} |{{#tag:ref|{{{1|{{{reference|{{{content|{{{text|}}}}}}}}}}}}|name={{{name|}}}|group={{#switch: {{{group|}}} | note | upper-alpha | upper-roman | lower-alpha | lower-greek | lower-roman = {{{group|}}} | #default = lower-alpha }} }} |{{#tag:ref|{{{1|{{{reference|{{{content|{{{text|}}}}}}}}}}}}|group={{#switch: {{{group|}}} | note | upper-alpha | upper-roman | lower-alpha | lower-greek | lower-roman = {{{group|}}} | #default = lower-alpha }} }} }}</includeonly><noinclude> {{documentation}} </noinclude> 6ed4e5c148014b92a23bd51d16f3180881bb876c Module:Sidebar/configuration 828 490 978 2023-03-14T22:32:24Z infoboxes>Izno 0 add hlist and plainlist_templatestyles keys Scribunto text/plain return { i18n = { child_yes = 'yes', float_none = 'none', float_left = 'left', wrap_true = 'true', navbar_none = 'none', navbar_off = 'off', default_list_title = 'List', title_not_to_add_navbar = 'Template:Sidebar', collapse_title_not_to_add_navbar = 'Template:Sidebar with collapsible lists', templatestyles = 'Module:Sidebar/styles.css', hlist_templatestyles = 'Hlist/styles.css', plainlist_templatestyles = 'Plainlist/styles.css', category = { child = '[[Category:Pages using sidebar with the child parameter]]', conversion = '[[Category:Sidebars with styles needing conversion]]' }, pattern = { collapse_sandbox = '/sandbox$', sandbox = '/sandbox$', subgroup = 'sidebar%-subgroup', style_conversion = 'style$', uncategorized_conversion_titles = { '/[Ss]andbox', '/[Tt]estcases', '/[Dd]oc$' } }, class = { sidebar = 'sidebar', subgroup = 'sidebar-subgroup', collapse = 'sidebar-collapse', float_none = 'sidebar-none', float_left = 'sidebar-left', wraplinks = 'nowraplinks', outer_title = 'sidebar-outer-title', top_image = 'sidebar-top-image', top_caption = 'sidebar-top-caption', pretitle = 'sidebar-pretitle', pretitle_with_top_image = 'sidebar-pretitle-with-top-image', title = 'sidebar-title', title_with_pretitle = 'sidebar-title-with-pretitle', image = 'sidebar-image', caption = 'sidebar-caption', above = 'sidebar-above', heading = 'sidebar-heading', content = 'sidebar-content', content_with_subgroup = 'sidebar-content-with-subgroup', below = 'sidebar-below', navbar = 'sidebar-navbar', list = 'sidebar-list', list_title = 'sidebar-list-title', list_title_centered = 'sidebar-list-title-c', list_content = 'sidebar-list-content' } } } dc2a980ac2162a898f7c21e6d6ba7e994dfeb315 Module:Sidebar 828 489 976 2023-03-14T22:35:53Z infoboxes>Izno 0 move these items to config, discovered during work at meta Scribunto text/plain require('strict') local cfg = mw.loadData('Module:Sidebar/configuration') local p = {} local getArgs = require('Module:Arguments').getArgs --[[ Categorizes calling templates and modules with a 'style' parameter of any sort for tracking to convert to TemplateStyles. TODO after a long cleanup: Catch sidebars in other namespaces than Template and Module. TODO would probably want to remove /log and /archive as CS1 does ]] local function categorizeTemplatesWithInlineStyles(args) local title = mw.title.getCurrentTitle() if title.namespace ~= 10 and title.namespace ~= 828 then return '' end for _, pattern in ipairs (cfg.i18n.pattern.uncategorized_conversion_titles) do if title.text:match(pattern) then return '' end end for key, _ in pairs(args) do if mw.ustring.find(key, cfg.i18n.pattern.style_conversion) or key == 'width' then return cfg.i18n.category.conversion end end end --[[ For compatibility with the original {{sidebar with collapsible lists}} implementation, which passed some parameters through {{#if}} to trim their whitespace. This also triggered the automatic newline behavior. ]] -- See ([[meta:Help:Newlines and spaces#Automatic newline]]) local function trimAndAddAutomaticNewline(s) s = mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1") if mw.ustring.find(s, '^[#*:;]') or mw.ustring.find(s, '^{|') then return '\n' .. s else return s end end --[[ Finds whether a sidebar has a subgroup sidebar. ]] local function hasSubgroup(s) if mw.ustring.find(s, cfg.i18n.pattern.subgroup) then return true else return false end end local function has_navbar(navbar_mode, sidebar_name) return navbar_mode ~= cfg.i18n.navbar_none and navbar_mode ~= cfg.i18n.navbar_off and ( sidebar_name or mw.getCurrentFrame():getParent():getTitle():gsub(cfg.i18n.pattern.sandbox, '') ~= cfg.i18n.title_not_to_add_navbar ) end local function has_list_class(args, htmlclass) local patterns = { '^' .. htmlclass .. '$', '%s' .. htmlclass .. '$', '^' .. htmlclass .. '%s', '%s' .. htmlclass .. '%s' } for arg, value in pairs(args) do if type(arg) == 'string' and mw.ustring.find(arg, 'class') then for _, pattern in ipairs(patterns) do if mw.ustring.find(args[arg] or '', pattern) then return true end end end end return false end -- there are a lot of list classes in the wild, so we add their TemplateStyles local function add_list_styles(args) local frame = mw.getCurrentFrame() local function add_list_templatestyles(htmlclass, templatestyles) if has_list_class(args, htmlclass) then return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } else return '' end end local plainlist_styles = add_list_templatestyles('plainlist', cfg.i18n.plainlist_templatestyles) local hlist_styles = add_list_templatestyles('hlist', cfg.i18n.hlist_templatestyles) -- a second workaround for [[phab:T303378]] -- when that issue is fixed, we can actually use has_navbar not to emit the -- tag here if we want if has_navbar(args.navbar, args.name) and hlist_styles == '' then hlist_styles = frame:extensionTag{ name = 'templatestyles', args = { src = cfg.i18n.hlist_templatestyles} } end -- hlist -> plainlist is best-effort to preserve old Common.css ordering. [hlist_note] return hlist_styles .. plainlist_styles end -- work around [[phab:T303378]] -- for each arg: find all the templatestyles strip markers, insert them into a -- table. then remove all templatestyles markers from the arg local function move_hiding_templatestyles(args) local gfind = string.gfind local gsub = string.gsub local templatestyles_markers = {} local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)' for k, arg in pairs(args) do for marker in gfind(arg, strip_marker_pattern) do table.insert(templatestyles_markers, marker) end args[k] = gsub(arg, strip_marker_pattern, '') end return templatestyles_markers end --[[ Main sidebar function. Takes the frame, args, and an optional collapsibleClass. The collapsibleClass is and should be used only for sidebars with collapsible lists, as in p.collapsible. ]] function p.sidebar(frame, args, collapsibleClass) if not args then args = getArgs(frame) end local hiding_templatestyles = table.concat(move_hiding_templatestyles(args)) local root = mw.html.create() local child = args.child and mw.text.trim(args.child) == cfg.i18n.child_yes root = root:tag('table') if not child then root :addClass(cfg.i18n.class.sidebar) -- force collapsibleclass to be sidebar-collapse otherwise output nothing :addClass(collapsibleClass == cfg.i18n.class.collapse and cfg.i18n.class.collapse or nil) :addClass('nomobile') :addClass(args.float == cfg.i18n.float_none and cfg.i18n.class.float_none or nil) :addClass(args.float == cfg.i18n.float_left and cfg.i18n.class.float_left or nil) :addClass(args.wraplinks ~= cfg.i18n.wrap_true and cfg.i18n.class.wraplinks or nil) :addClass(args.bodyclass or args.class) :css('width', args.width or nil) :cssText(args.bodystyle or args.style) if args.outertitle then root :tag('caption') :addClass(cfg.i18n.class.outer_title) :addClass(args.outertitleclass) :cssText(args.outertitlestyle) :wikitext(args.outertitle) end if args.topimage then local imageCell = root:tag('tr'):tag('td') imageCell :addClass(cfg.i18n.class.top_image) :addClass(args.topimageclass) :cssText(args.topimagestyle) :wikitext(args.topimage) if args.topcaption then imageCell :tag('div') :addClass(cfg.i18n.class.top_caption) :cssText(args.topcaptionstyle) :wikitext(args.topcaption) end end if args.pretitle then root :tag('tr') :tag('td') :addClass(args.topimage and cfg.i18n.class.pretitle_with_top_image or cfg.i18n.class.pretitle) :addClass(args.pretitleclass) :cssText(args.basestyle) :cssText(args.pretitlestyle) :wikitext(args.pretitle) end else root :addClass(cfg.i18n.class.subgroup) :addClass(args.bodyclass or args.class) :cssText(args.bodystyle or args.style) end if args.title then if child then root :wikitext(args.title) else root :tag('tr') :tag('th') :addClass(args.pretitle and cfg.i18n.class.title_with_pretitle or cfg.i18n.class.title) :addClass(args.titleclass) :cssText(args.basestyle) :cssText(args.titlestyle) :wikitext(args.title) end end if args.image then local imageCell = root:tag('tr'):tag('td') imageCell :addClass(cfg.i18n.class.image) :addClass(args.imageclass) :cssText(args.imagestyle) :wikitext(args.image) if args.caption then imageCell :tag('div') :addClass(cfg.i18n.class.caption) :cssText(args.captionstyle) :wikitext(args.caption) end end if args.above then root :tag('tr') :tag('td') :addClass(cfg.i18n.class.above) :addClass(args.aboveclass) :cssText(args.abovestyle) :newline() -- newline required for bullet-points to work :wikitext(args.above) end local rowNums = {} for k, v in pairs(args) do k = '' .. k local num = k:match('^heading(%d+)$') or k:match('^content(%d+)$') if num then table.insert(rowNums, tonumber(num)) end end table.sort(rowNums) -- remove duplicates from the list (e.g. 3 will be duplicated if both heading3 -- and content3 are specified) for i = #rowNums, 1, -1 do if rowNums[i] == rowNums[i - 1] then table.remove(rowNums, i) end end for i, num in ipairs(rowNums) do local heading = args['heading' .. num] if heading then root :tag('tr') :tag('th') :addClass(cfg.i18n.class.heading) :addClass(args.headingclass) :addClass(args['heading' .. num .. 'class']) :cssText(args.basestyle) :cssText(args.headingstyle) :cssText(args['heading' .. num .. 'style']) :newline() :wikitext(heading) end local content = args['content' .. num] if content then root :tag('tr') :tag('td') :addClass(hasSubgroup(content) and cfg.i18n.class.content_with_subgroup or cfg.i18n.class.content) :addClass(args.contentclass) :addClass(args['content' .. num .. 'class']) :cssText(args.contentstyle) :cssText(args['content' .. num .. 'style']) :newline() :wikitext(content) :done() -- Without a linebreak after the </td>, a nested list like -- "* {{hlist| ...}}" doesn't parse correctly. :newline() end end if args.below then root :tag('tr') :tag('td') :addClass(cfg.i18n.class.below) :addClass(args.belowclass) :cssText(args.belowstyle) :newline() :wikitext(args.below) end if not child and has_navbar(args.navbar, args.name) then root :tag('tr') :tag('td') :addClass(cfg.i18n.class.navbar) :cssText(args.navbarstyle) :wikitext(require('Module:Navbar')._navbar{ args.name, mini = 1, fontstyle = args.navbarfontstyle }) end local base_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = cfg.i18n.templatestyles } } local templatestyles = '' if args['templatestyles'] and args['templatestyles'] ~= '' then templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['templatestyles'] } } end local child_templatestyles = '' if args['child templatestyles'] and args['child templatestyles'] ~= '' then child_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['child templatestyles'] } } end local grandchild_templatestyles = '' if args['grandchild templatestyles'] and args['grandchild templatestyles'] ~= '' then grandchild_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['grandchild templatestyles'] } } end return table.concat({ add_list_styles(args), -- see [hlist_note] above about ordering base_templatestyles, templatestyles, child_templatestyles, grandchild_templatestyles, hiding_templatestyles, tostring(root), (child and cfg.i18n.category.child or ''), categorizeTemplatesWithInlineStyles(args) }) end local function list_title(args, is_centered_list_titles, num) local title_text = trimAndAddAutomaticNewline(args['list' .. num .. 'title'] or cfg.i18n.default_list_title) local title if is_centered_list_titles then -- collapsible can be finicky, so provide some CSS/HTML to support title = mw.html.create('div') :addClass(cfg.i18n.class.list_title_centered) :wikitext(title_text) else title = mw.html.create() :wikitext(title_text) end local title_container = mw.html.create('div') :addClass(cfg.i18n.class.list_title) -- don't /need/ a listnumtitleclass because you can do -- .templateclass .listnumclass .sidebar-list-title :addClass(args.listtitleclass) :cssText(args.basestyle) :cssText(args.listtitlestyle) :cssText(args['list' .. num .. 'titlestyle']) :node(title) :done() return title_container end --[[ Main entry point for sidebar with collapsible lists. Does the work of creating the collapsible lists themselves and including them into the args. ]] function p.collapsible(frame) local args = getArgs(frame) if not args.name and frame:getParent():getTitle():gsub(cfg.i18n.pattern.collapse_sandbox, '') == cfg.i18n.collapse_title_not_to_add_navbar then args.navbar = cfg.i18n.navbar_none end local contentArgs = {} local is_centered_list_titles = false if args['centered list titles'] and args['centered list titles'] ~= '' then is_centered_list_titles = true end for k, v in pairs(args) do local num = string.match(k, '^list(%d+)$') if num then local expand = args.expanded and (args.expanded == 'all' or args.expanded == args['list' .. num .. 'name']) local row = mw.html.create('div') row :addClass(cfg.i18n.class.list) :addClass('mw-collapsible') :addClass((not expand) and 'mw-collapsed' or nil) :addClass(args['list' .. num .. 'class']) :cssText(args.listframestyle) :cssText(args['list' .. num .. 'framestyle']) :node(list_title(args, is_centered_list_titles, num)) :tag('div') :addClass(cfg.i18n.class.list_content) :addClass('mw-collapsible-content') -- don't /need/ a listnumstyleclass because you can do -- .templatename .listnumclass .sidebar-list :addClass(args.listclass) :cssText(args.liststyle) :cssText(args['list' .. num .. 'style']) :wikitext(trimAndAddAutomaticNewline(args['list' .. num])) contentArgs['content' .. num] = tostring(row) end end for k, v in pairs(contentArgs) do args[k] = v end return p.sidebar(frame, args, cfg.i18n.class.collapse) end return p 71fe765846593e025ca2f94371315e9dbb5bb4d2 Module:Find sources/config 828 427 854 2023-03-16T03:37:43Z wikipedia>Trialpears 0 Changed protection settings for "[[Module:Find sources/config]]": [[WP:High-risk templates|Highly visible template]] Lots more transclusions due to inclusion in Talk header a year ago. ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain -- Configuration data for [[Module:Find sources]]. return { -- Define the error message and category to be used if the module is used in -- the main namespace and the template config doesn't set the -- isUsedInMainspace key to true. The category is optional; if it is not -- wanted, it can be removed. ['namespace-error'] = 'Error: Please do not use this template in articles.', ['namespace-error-category'] = 'Pages with templates in the wrong namespace', -- The separator to be used if no separator is specified in the template -- config. ['default-separator'] = mw.message.new('Dot-separator'):plain() } 412ea800b842be74cd74e8289e346e8b30b1df91 Template:Div col/styles.css 10 322 639 2023-03-29T18:11:31Z wikipedia>Jonesey95 0 Attempt to copy plainlist class from [[MediaWiki:Common.css]]. I don't know if this actually works. text text/plain /* {{pp|small=yes}} */ .div-col { margin-top: 0.3em; column-width: 30em; } .div-col-small { font-size: 90%; } .div-col-rules { column-rule: 1px solid #aaa; } /* Reset top margin for lists in div col */ .div-col dl, .div-col ol, .div-col ul { margin-top: 0; } /* Avoid elements breaking between columns See also Template:No col break */ .div-col li, .div-col dd { page-break-inside: avoid; /* Removed from CSS in favor of break-inside c. 2020 */ break-inside: avoid-column; } /* Unbulleted lists */ .plainlist ol, .plainlist ul { line-height: inherit; list-style: none; margin: 0; } .plainlist ol li, .plainlist ul li { margin-bottom: 0; } 76c2c0a042b9164cff638cd44af5ab129702c141 Template:Infobox/user style 10 339 673 2023-04-03T14:00:08Z wikipedia>Maddy from Celeste 0 blatant self-promotion wikitext text/x-wiki {{{heading| ==Infoboxes and user style == }}} Users can have [[WP:User style|user CSS]] that hides<!--, moves, or makes collapsible--> any infoboxes in their own browsers. To hide all infoboxes, add the following to [[Special:MyPage/common.css]] (for all [[WP:Skin|skins]], or [[Special:MyPage/skin.css]] for just the current skin), on a line by itself: <syntaxhighlight lang="css">div.mw-parser-output .infobox { display: none; }</syntaxhighlight> Alternatively, you can add the following code to [[Special:MyPage/common.js|your common.js]] or into a browser user script that is executed by an extension like [[Greasemonkey]]: <syntaxhighlight lang="js">$('.infobox').hide();</syntaxhighlight> Be aware that although{{#if:{{{guideline|}}}||, per [[WP:Manual of Style/Infoboxes]],}} all information in an infobox ideally should also be found in the main body of an article, there isn't perfect compliance with this guideline. For example, the full taxonomic hierarchy in {{tlx|Taxobox}}, and the OMIM and other medical database codes of {{tlx|Infobox disease}} are often not found in the main article content. The infobox is also often the location of the most significant, even only, image in an article. There is a userscript which removes infoboxes but moves the images contained to separate thumbnails: [[User:Maddy from Celeste/disinfobox.js]].<!-- Needs Special:Mypage/common.js options for: * Making infoboxes collapsible ** Making them auto-collapsed * Moving infoboxes to bottom of page --><noinclude> {{Documentation|content= This documentation snippet is transcluded at [[Help:Infobox]], [[Template:Infobox/doc]], [[WP:Customisation#Hiding specific messages]], [[Help:User style]], [[WP:Manual of Style/Infoboxes]], and other places where this information is relevant. As a template, this snippet takes a {{para|heading}} parameter to replace the level-2 <code>==Infoboxes and user style==</code> section heading code, as needed. E.g., for a <code>=== ... ===</code> level-3 heading: <code><nowiki>heading={{=}}{{=}}{{=}}Infoboxes and user style{{=}}{{=}}{{=}}</nowiki></code> }} </noinclude> ba4dac68eb2bdc49a32f2a11b9afd52381bf06b5 Help:Infobox/user style 12 493 984 2023-04-03T14:00:08Z infoboxes>Maddy from Celeste 0 blatant self-promotion wikitext text/x-wiki {{{heading| ==Infoboxes and user style == }}} Users can have [[WP:User style|user CSS]] that hides<!--, moves, or makes collapsible--> any infoboxes in their own browsers. To hide all infoboxes, add the following to [[Special:MyPage/common.css]] (for all [[WP:Skin|skins]], or [[Special:MyPage/skin.css]] for just the current skin), on a line by itself: <syntaxhighlight lang="css">div.mw-parser-output .infobox { display: none; }</syntaxhighlight> Alternatively, you can add the following code to [[Special:MyPage/common.js|your common.js]] or into a browser user script that is executed by an extension like [[Greasemonkey]]: <syntaxhighlight lang="js">$('.infobox').hide();</syntaxhighlight> Be aware that although{{#if:{{{guideline|}}}||, per [[WP:Manual of Style/Infoboxes]],}} all information in an infobox ideally should also be found in the main body of an article, there isn't perfect compliance with this guideline. For example, the full taxonomic hierarchy in {{tlx|Taxobox}}, and the OMIM and other medical database codes of {{tlx|Infobox disease}} are often not found in the main article content. The infobox is also often the location of the most significant, even only, image in an article. There is a userscript which removes infoboxes but moves the images contained to separate thumbnails: [[User:Maddy from Celeste/disinfobox.js]].<!-- Needs Special:Mypage/common.js options for: * Making infoboxes collapsible ** Making them auto-collapsed * Moving infoboxes to bottom of page --><noinclude> {{Documentation|content= This documentation snippet is transcluded at [[Help:Infobox]], [[Template:Infobox/doc]], [[WP:Customisation#Hiding specific messages]], [[Help:User style]], [[WP:Manual of Style/Infoboxes]], and other places where this information is relevant. As a template, this snippet takes a {{para|heading}} parameter to replace the level-2 <code>==Infoboxes and user style==</code> section heading code, as needed. E.g., for a <code>=== ... ===</code> level-3 heading: <code><nowiki>heading={{=}}{{=}}{{=}}Infoboxes and user style{{=}}{{=}}{{=}}</nowiki></code> }} </noinclude> ba4dac68eb2bdc49a32f2a11b9afd52381bf06b5 Module:Uses TemplateStyles 828 371 736 2023-04-18T22:22:16Z wikipedia>Grufo 0 Move the preview inside the documentation wikitext text/x-wiki <includeonly>{{#invoke:Uses TemplateStyles|main}}</includeonly><noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> 60f2fc73c4d69b292455879f9fcb3c68f6c63c2a Template:Uses TemplateStyles 10 380 756 2023-04-18T22:22:16Z wikipedia>Grufo 0 Move the preview inside the documentation wikitext text/x-wiki <includeonly>{{#invoke:Uses TemplateStyles|main}}</includeonly><noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> 60f2fc73c4d69b292455879f9fcb3c68f6c63c2a Module:Convert/text 828 498 996 2023-04-19T04:48:47Z string2>Johnuniq 0 update from sandbox per [[Template talk:Convert#Module version 28]] Scribunto text/plain -- Text used by Module:Convert for enwiki. -- This is a separate module to simplify translation for use on another wiki. -- See [[:en:Template:Convert/Transwiki guide]] if copying to another wiki. -- Some units accept an SI prefix before the unit code, such as "kg" for kilogram. local SIprefixes = { -- The prefix field is what the prefix should be, if different from the prefix used. ['Q'] = { exponent = 30, name = 'quetta', }, ['R'] = { exponent = 27, name = 'ronna', }, ['Y'] = { exponent = 24, name = 'yotta', }, ['Z'] = { exponent = 21, name = 'zetta', }, ['E'] = { exponent = 18, name = 'exa' , }, ['P'] = { exponent = 15, name = 'peta' , }, ['T'] = { exponent = 12, name = 'tera' , }, ['G'] = { exponent = 9, name = 'giga' , }, ['M'] = { exponent = 6, name = 'mega' , }, ['k'] = { exponent = 3, name = 'kilo' , }, ['h'] = { exponent = 2, name = 'hecto', }, ['da']= { exponent = 1, name = 'deca' , name_us = 'deka' }, ['d'] = { exponent = -1, name = 'deci' , }, ['c'] = { exponent = -2, name = 'centi', }, ['m'] = { exponent = -3, name = 'milli', }, ['μ'] = { exponent = -6, name = 'micro', }, -- key = 'GREEK SMALL LETTER MU' (U+03BC) utf-8 CE BC ['µ'] = { exponent = -6, name = 'micro', prefix = 'μ' }, -- key = 'MICRO SIGN' (U+00B5) utf-8 C2 B5 ['u'] = { exponent = -6, name = 'micro', prefix = 'μ' }, -- not an SI prefix, but allow for people typing this ['n'] = { exponent = -9, name = 'nano' , }, ['p'] = { exponent =-12, name = 'pico' , }, ['f'] = { exponent =-15, name = 'femto', }, ['a'] = { exponent =-18, name = 'atto' , }, ['z'] = { exponent =-21, name = 'zepto', }, ['y'] = { exponent =-24, name = 'yocto', }, ['r'] = { exponent =-27, name = 'ronto', }, ['q'] = { exponent =-30, name = 'quecto', }, } -- Some units can be qualified with one of the following prefixes, when linked. local customary_units = { { "US", link = "United States customary units" }, { "U.S.", link = "United States customary units" }, { "imperial", link = "Imperial units" }, { "imp", link = "Imperial units" }, } -- Names when using engineering notation (a prefix of "eN" where N is a number; example "e6km"). -- key = { "name", link = "article title", exponent = numeric_key_value } -- If lk=on and link is defined, the name of the number will appear as a link. local eng_scales = { ["3"] = { "thousand", exponent = 3 }, ["6"] = { "million", exponent = 6 }, ["9"] = { "billion", link = "1000000000 (number)", exponent = 9 }, ["12"] = { "trillion", link = "1000000000000 (number)", exponent = 12 }, ["15"] = { "quadrillion", link = "1000000000000000 (number)", exponent = 15 }, } local all_categories = { unit = "[[Category:Convert errors]]", option = "[[Category:Convert errors]]", warning = '[[Category:Convert invalid options]]', tracking = '[[Category:Convert tracking]]', } -- For some error messages, the following puts the wanted style around -- each unit code marked like '...%{ft%}...'. local unitcode_regex = '%%([{}])' local unitcode_replace = { ['{'] = '"', ['}'] = '"' } -- no longer need the more elaborate substitute used before 2013-09-28 -- All messages that may be displayed if a problem occurs. local all_messages = { -- Message format string: $1=title, $2=text, $3=category, $4=anchor. -- Each displayed message starts with "Convert:" so can easily locate by searching article. cvt_format = '<sup class="noprint Inline-Template" style="white-space:nowrap;">[<i>[[Help:Convert messages#$4|<span title="Convert: $1">convert: $2</span>]]</i>]</sup>$3<span class="error"></span>', cvt_format2 = '<sup class="noprint Inline-Template" style="white-space:nowrap;">[[Help:Convert messages#$4|<span title="Convert: $1">$2</span>]]</sup>$3<span class="error"></span>', cvt_format_preview = '<strong class="error">Error in convert: $1 [[Help:Convert messages#$4|(help)]]</strong>$3', -- Each of following messages is a table: -- { [1] = 'title', -- mouseover title text -- [2] = 'text', -- link text displayed in article -- [3] = 'category key', -- key to lookup category in all_categories -- [4] = 'anchor', -- anchor for link to relevant section on help page -- regex = gsub_regex, -- replace = gsub_table, -- } Mouseover title text Link text CatKey Anchor cvt_bad_input = { 'input "$1" must be a number and unit' , 'invalid input' , 'option', 'invalid_input' }, cvt_bad_num = { 'Value "$1" must be a number' , 'invalid number' , 'option', 'invalid_number' }, cvt_big_prec = { 'Precision "$1" is too large' , 'precision too large' , 'option', 'precision_too_large' }, cvt_invalid_num = { 'Number has overflowed' , 'number overflow' , 'option', 'number_overflow' }, cvt_no_num = { 'Needs the number to be converted' , 'needs a number' , 'option', 'needs_number' }, cvt_no_num2 = { 'Needs another number for a range' , 'needs another number', 'option', 'needs_another_number' }, cvt_bad_altitude = { '"$1" needs an integer' , 'invalid altitude' , 'option', 'invalid_altitude' }, cvt_bad_frac = { '"$1" needs an integer above 1' , 'invalid fraction' , 'option', 'invalid_fraction' }, cvt_bad_prec = { 'Precision "$1" must be an integer' , 'invalid precision' , 'option', 'invalid_precision' }, cvt_bad_sigfig = { '"$1" needs a positive integer' , 'invalid sigfig' , 'option', 'invalid_sigfig' }, cvt_empty_option = { 'Ignored empty option "$1"' , 'empty option' , 'option', 'empty_option' }, cvt_deprecated = { 'Option "$1" is deprecated' , '*' , 'option', 'deprecated_option', format = 'cvt_format2', nowarn = true }, cvt_no_spell = { 'Spelling is not available' , 'bug, ask for help' , 'option', 'ask_for_help' }, cvt_unknown_option = { 'Ignored invalid option "$1"' , 'invalid option' , 'option', 'invalid_option' }, cvt_wd_fail = { 'Unable to access Wikidata' , 'wikidata problem' , 'option', 'wikidata_problem' }, cvt_bad_default = { 'Unit "$1" has an invalid default' , 'bug, ask for help' , 'unit' , 'ask_for_help' }, cvt_bad_unit = { 'Unit "$1" is invalid here' , 'unit invalid here' , 'unit' , 'unit_invalid_here' }, cvt_no_default = { 'Unit "$1" has no default output unit' , 'bug, ask for help' , 'unit' , 'ask_for_help' }, cvt_no_unit = { 'Needs name of unit' , 'needs unit name' , 'unit' , 'needs_unit_name' }, cvt_unknown = { 'Unit name "$1" is not known' , 'unknown unit' , 'unit' , 'unknown_unit' }, cvt_should_be = { '$1' , 'ambiguous unit' , 'unit' , 'ambiguous_unit', regex = unitcode_regex, replace = unitcode_replace }, cvt_mismatch = { 'Cannot convert "$1" to "$2"' , 'unit mismatch' , 'unit' , 'unit_mismatch' }, cvt_bug_convert = { 'Bug: Cannot convert between specified units', 'bug, ask for help' , 'unit' , 'ask_for_help' }, cvt_lookup = { 'Unit "$1" is incorrectly defined' , 'bug, ask for help' , 'unit' , 'ask_for_help' }, } -- Text to join input value/unit with output value/unit. local disp_joins = { -- [1]=before output, [2]=after output, [3]=between outputs in a combination; default "; " -- [wantname] gives default abbr=off ["or"] = { " or " , "" , " or ", wantname = true }, ["sqbr-sp"] = { " [" , "]" }, ["sqbr-nbsp"] = { "&nbsp;[" , "]" }, ["comma"] = { ", " , "" , ", " }, ["semicolon"] = { "; " , "" }, ["slash-sp"] = { " / " , "" , wantname = true }, ["slash-nbsp"] = { "&nbsp;/ ", "" , wantname = true }, ["slash-nosp"] = { "/" , "" , wantname = true }, ["b"] = { " (" , ")" }, ["(or)"] = { " (" , ")", " or " }, ["br"] = { "<br />" , "" , wantname = true }, ["br()"] = { "<br />(" , ")", wantname = true }, } -- Text to separate values in a range. local range_types = { -- Specifying a table requires either: -- * "off" and "on" values (for "abbr=off" and "abbr=on"), or -- * "input" and "output" values (for LHS and RHS); -- other fields are optional. -- When "adj=on|abbr=off" applies, spaces in range text are replaced with hyphens. -- With "exception = true", that also occurs with "adj=on|abbr=on". -- If "adj" is defined here, that text (unchanged) is used with "adj=on". ["+"] = " + ", [","] = ",&nbsp;", [", and"] = ", and ", [", or"] = ", or ", ["by"] = " by ", ["-"] = "–", ["to about"] = " to about ", ["and"] = { off = " and ", on = " and ", exception = true }, ["and(-)"] = { input = " and ", output = "–" }, ["or"] = { off = " or " , on = " or " , exception = true }, ["to"] = { off = " to " , on = " to " , exception = true }, ["to(-)"] = { input = "&nbsp;to ", output = "–" }, ["+/-"] = { off = "&nbsp;±&nbsp;", on = "&nbsp;±&nbsp;", adj = "&nbsp;±&nbsp;", is_range_change = true }, ["by(x)"] = { input = " by ", output = " ×&nbsp;", out_range_x = true }, ["x"] = { off = " by ", on = " ×&nbsp;", abbr_range_x = true }, ["xx"] = "&nbsp;×&nbsp;", ["*"] = "×", ["/"] = "&thinsp;/&thinsp;", -- for a table of high/low temperatures with {{convert|83|/|63|F|disp=br()|abbr=values}} } local range_aliases = { -- ["alternative name for a range"] = "standard range name" ["–"] = "-", ["&ndash;"] = "-", ["×"] = "x", ["&times;"] = "x", ["±"] = "+/-", ["&plusmn;"] = "+/-", } -- Convert accepts range text delimited with whitespace, for example, {{convert|1 to 2|ft}}. -- In addition, the following "words" are accepted without spaces, for example, {{convert|1-2|ft}}. -- Words must be in correct order for searching, for example, 'x' after 'xx'. local range_words = { '-', '–', 'xx', 'x', '*' } local ranges = { types = range_types, aliases = range_aliases, words = range_words, } -- Valid option names. local en_option_name = { -- ["local text for option name"] = "en name used in this module" ["$"] = "$", ["abbr"] = "abbr", ["adj"] = "adj", ["altitude_ft"] = "altitude_ft", ["altitude_m"] = "altitude_m", ["comma"] = "comma", ["debug"] = "debug", ["disp"] = "disp", ["frac"] = "frac", ["input"] = "input", ["lang"] = "lang", ["lk"] = "lk", ["order"] = "order", ["qid"] = "qid", ["qual"] = "qual", ["qualifier"] = "qual", ["round"] = "round", ["sigfig"] = "sigfig", ["sing"] = "adj", -- "sing" is an old alias for "adj" ["sortable"] = "sortable", ["sp"] = "sp", ["spell"] = "spell", ["stylein"] = "stylein", ["styleout"] = "styleout", ["tracking"] = "tracking", } -- Valid option values. -- Convention: parms.opt_xxx refers to an option that is set here -- (not intended to be set by the template which invokes this module). -- Example: At enwiki, "abbr" includes: -- ["values"] = "opt_values" -- As a result, if the template uses abbr=values, Module:Convert sets: -- parms["opt_values"] = true -- parms["abbr"] = nil -- Therefore parms.abbr will be nil, or will have one of the listed values -- that do not start with "opt_". -- An option value of form "xxx?" is the same as "xxx" but shows the input as deprecated. local en_option_value = { ["$"] = 'TEXT', -- TEXT should be a currency symbol that will be used instead of "$" ["abbr"] = { -- ["local text for option value"] = "en value used in this module" ["def"] = "", -- ignored (some wrapper templates call convert with "abbr=def" to mean "default abbreviation") ["h"] = "on", -- abbr=on + use "h" for hand unit (default) ["hh"] = "opt_hand_hh", -- abbr=on + use "hh" for hand unit ["in"] = "in", -- use symbol for LHS unit ["none"] = "off", -- old name for "off" ["off"] = "off", -- use name for all units ["on"] = "on", -- use symbol for all units ["out"] = "out", -- use symbol for RHS unit (default) ["unit"] = "unit", -- abbr=on but abbreviate units only: e6km → million km (not ×10⁶ km) ["values"] = "opt_values", -- show only input and output numbers, not units ["~"] = "opt_also_symbol", -- show input unit symbol as well as name }, ["adj"] = { ["mid"] = "opt_adjectival, opt_adj_mid", -- adj=on with user-specified text after input unit (between input and output) ["off"] = "", -- ignored (off is the default) ["on"] = "opt_adjectival", -- unit name is singular and hyphenated ["pre"] = "opt_one_preunit", -- user-specified text before input unit ["ri0"] = "opt_ri=0", -- round input with precision = 0 ["ri1"] = "opt_ri=1", -- round input with precision = 1 ["ri2"] = "opt_ri=2", -- round input with precision = 2 ["ri3"] = "opt_ri=3", -- round input with precision = 3 }, ["altitude_ft"] = 'INTEGER', ["altitude_m"] = 'INTEGER', ["comma"] = { ["5"] = "opt_comma5", -- only use numsep grouping if 5 or more digits ["gaps"] = "opt_gaps", -- use gaps, not numsep, to separate groups of digits ["gaps3"] = "opt_gaps, opt_gaps3", -- group only in threes rather than default of no gap before a single digit after decimal mark ["off"] = "opt_nocomma", -- no numsep in input or output numbers }, ["debug"] = { ["yes"] = "opt_sortable_debug", -- make the normally hidden sort key visible }, ["disp"] = { ["5"] = "opt_round=5?", -- round output value to nearest 5 ["b"] = "b", -- join: '(...)' ["(or)"] = "(or)", -- join: '(...)' with 'or' between outputs in a combination ["br"] = "br", -- join: '<br />' ["br()"] = "br()", -- join: '<br />(...)' ["comma"] = "comma", -- join: ',' ["flip"] = "opt_flip", -- reverse order of input/output ["number"] = "opt_output_number_only", -- display output value (not input, and not output symbol/name) ["or"] = "or", -- join: 'or' ["out"] = "opt_output_only", ["output number only"] = "opt_output_number_only", ["output only"] = "opt_output_only", ["preunit"] = "opt_two_preunits", -- user-specified text before input and output units ["semicolon"] = "semicolon", -- join: ';' ["sqbr"] = "sqbr", -- join: '[...]' ["table"] = "opt_table", -- output is suitable for a table cell with align="right" ["tablecen"] = "opt_tablecen", -- output is suitable for a table cell with align="center" ["unit"] = "opt_input_unit_only", -- display input symbol/name (not output, and not input value) ["unit or text"] = "opt_input_unit_only, opt_ignore_error", -- display input symbol/name, or given unit code if not known ["unit2"] = "opt_output_unit_only", ["x"] = "x", -- join: <first>...<second> (user-specified text) }, ["frac"] = 'INTEGER', ["input"] = 'TEXT', -- TEXT should be value><space><unitcode> or <wikidata-property-id> ["lang"] = { -- language for output digits (both en and local digits are always accepted for input) ["en"] = "opt_lang_en", -- use en digits for numbers, regardless of local language ["local"] = "opt_lang_local", -- use local digits for numbers (default, although config can change default to en) }, ["lk"] = { ["in"] = "in", -- link LHS unit name or symbol ["off"] = "off", -- do not link: same as default except for hand unit ["on"] = "on", -- link all unit names or symbols (but not twice for the same unit) ["out"] = "out", -- link RHS unit name or symbol }, ["order"] = { ["flip"] = "opt_flip", -- reverse order of input/output ["out"] = "opt_order_out", -- do not show input; instead, use order in output combination, with the first output shown as the input }, ["qid"] = 'TEXT', -- TEXT should be a Wikidata Q item identifier ["qual"] = 'TEXT', -- TEXT should be a Wikidata Q item identifier ["round"] = { ["0.5"] = "opt_round=0.5", -- round output value to nearest 0.5 ["5"] = "opt_round=5", -- round output value to nearest 5 ["10"] = "opt_round=10", -- round output value to nearest 10 (same as but clearer than "|-1") ["25"] = "opt_round=25", -- round output value to nearest 25 ["50"] = "opt_round=50", -- round output value to nearest 50 ["each"] = "opt_round_each", -- using default precision in a range, round each output separately (default uses highest precision of each item in range) }, ["sigfig"] = 'INTEGER', ["sortable"] = { ["off"] = "", -- ignored (off is the default) ["on"] = "opt_sortable_on", -- output sort key for use in a sortable table, based on value from converting to a standard base unit ["debug"] = "opt_sortable_on, opt_sortable_debug", -- |sortable=debug is the same as |sortable=on|debug=yes }, ["sp"] = { ["us"] = "opt_sp_us", -- use U.S. spelling (like "meter" instead of default "metre") }, ["spell"] = { -- only English spelling is supported; not scientific notation; only some fractions ["in"] = "opt_spell_in", -- spell input value in words ["In"] = "opt_spell_in, opt_spell_upper", -- spell input value in words with first letter uppercase ["on"] = "opt_spell_in, opt_spell_out", -- spell input and output values in words ["On"] = "opt_spell_in, opt_spell_out, opt_spell_upper", -- same, with first letter of first word in result uppercase ["us"] = "opt_sp_us", -- use U.S. spelling; same as sp=us so spell=us also works }, ["stylein"] = 'TEXT', ["styleout"] = 'TEXT', ["tracking"] = 'TEXT', } local titles = { ["frac"] = "Fraction/styles.css", ["sfrac"] = "Sfrac/styles.css", } return { SIprefixes = SIprefixes, all_categories = all_categories, all_messages = all_messages, currency = { ['$'] = true, ['£'] = true, ['€'] = true, ['₱'] = true, ['₽'] = true, ['¥'] = true }, customary_units = customary_units, disp_joins = disp_joins, en_option_name = en_option_name, en_option_value = en_option_value, eng_scales = eng_scales, ranges = ranges, titles = titles, } ff98cf24da87736f3469f82401084ca608335d55 Template:More citations needed 10 453 906 2023-04-19T22:52:12Z wikipedia>Paine Ellsworth 0 per edit request on talk page - for general usage and specifically for [[:Template:More citations needed section]] - specify "in this section" or other name in first parameter wikitext text/x-wiki {{SAFESUBST:<noinclude />#invoke:Unsubst||date=__DATE__ |$B= {{Ambox | name = {{{name|More citations needed}}} | small = {{#if:{{{small|}}}|left}} | type = content | class = ambox-Refimprove | image = [[File:Question book-new.svg|50x40px|alt=]] | issue = This {{#if:{{{1|}}}|{{{1}}}|article}} '''needs additional citations for [[Wikipedia:Verifiability|verification]]'''. | fix = Please help [{{fullurl:{{FULLPAGENAME}}|action=edit}} improve this article] by [[Help:Referencing for beginners|adding citations to reliable sources]]{{#if:{{{1|}}}|{{sp}}in this {{{1}}}}}. Unsourced material may be challenged and removed.{{#if:{{{find2|{{{unquoted|}}}}}}| <br /><small>{{find sources mainspace|{{#if:{{{find|}}}|{{{find}}}|.}}|{{{find2|{{{unquoted|}}}}}}}}</small> |{{#if:{{{find|}}}|{{#ifeq: {{{find|}}} |none ||<br /><small>{{find sources mainspace|{{{find}}} }}</small>}}|<br /><small>{{find sources mainspace}}</small>}} }} | removalnotice = yes | talk = {{{talk|}}} | date = {{{date|}}} | cat = Articles needing additional references | all = All articles needing additional references }} }}<noinclude> <!-- Please add categories to the /doc subpage, thanks --> {{Documentation}} </noinclude> 81086bb304d099fd880ac87f42069dd039303c05 Module:Lx 828 516 1040 2023-04-20T01:08:11Z string2>Plastikspork 0 [[Wikipedia:Templates for discussion/Log/2023 April 10#Internal link namespace-specific templates]] closed as no consensus ([[WP:XFDC#4.0.13|XFDcloser]]) wikitext text/x-wiki <includeonly><span class="plainlinks nourlexpansion lx"><!-- -->[[{{{1}}}{{{2}}}]]&nbsp;<!-- --><span style="font-size:90%;">(<!-- -->{{#if:{{{noedit|}}}||[{{fullurl:{{{1}}}{{{2}}}|action=edit}} edit] &#124;}} <!-- -->{{#if:{{{notalk|}}}||[[{{{3}}}{{{5|:}}}{{{2}}}|{{{4}}}]] &#124;}} <!-- -->{{#if:{{{nohistory|}}}||[{{fullurl:{{{1}}}{{{2}}}|action=history}} history] &#124;}} <!-- -->{{#if:{{{nolinks|}}}||[{{fullurl:Special:Whatlinkshere/{{{1}}}{{{2}}}}} links] &#124;}} <!-- -->{{#if:{{{nowatch|}}}||[{{fullurl:{{{1}}}{{{2}}}|action=watch}} watch] &#124;}} <!-- -->{{#if:{{{nologs|}}}||[{{fullurl:Special:Log|page={{urlencode:{{{1}}}{{{2}}}}}}} logs]}}<!-- -->)</span></span><!-- --></includeonly><noinclude> {{Documentation}} </noinclude> 0ff233c8941d4624bed30222e0d2ae8ac142c899 Module:Lmd 828 523 1054 2023-04-20T01:08:11Z string2>Plastikspork 0 [[Wikipedia:Templates for discussion/Log/2023 April 10#Internal link namespace-specific templates]] closed as no consensus ([[WP:XFDC#4.0.13|XFDcloser]]) wikitext text/x-wiki {{lx|1=Module:|2={{ucfirst:{{{1}}}}}|3=Module talk|4=talk}}<noinclude> {{documentation|Template:Ln/doc}} </noinclude> ada2a915a84c0f0ea7b317cbf6bfe01c28004751 Module:Video game wikidata 828 472 944 2023-04-20T13:49:48Z wikipedia>Ferret 0 Add PS5/XSXS records Scribunto text/plain local Date = require('Module:Date')._Date local yesno = require('Module:Yesno') local p = {} -- Local variables. local reviewer = nil; local df = "mdy"; local entity = nil; local genRefs = true; local showSystem = true; local showUpdateLink = true; local system = nil; local systemId = nil; local systemFormat = "colon"; local updateLinkStyle = nil; local entities = {}; -- Translation table for converting numeric-IDs to shorthand aliases. local systemAliases = { [10677] = 'PS1', [1323662] = 'PS1', -- Placeholder, this is actually the series but could be mistakenly used for PS1. [10680] = 'PS2', [10683] = 'PS3', [5014725] = 'PS4', [16338] = 'PC', [8079] = 'Wii', [56942] = 'WiiU', [132020] = 'XBOX', [48263] = 'X360', [13361286] = 'XONE', [203597] = '3DS', [188808] = 'PSV', [170323] = 'DS', -- Sometimes has been NDS [170325] = 'PSP', [48493] = 'IOS', -- iOS, iPhone, iPad [94] = 'AND', -- Android [186437] = 'GB', [188642] = 'GBA', [203992] = 'GBC', [184198] = 'DC', [200912] = 'SAT', [172742] = 'NES', [183259] = 'SNES', [184839] = 'N64', [182172] = 'GC', -- Sometimes has been NGC [19610114] = 'NS', -- Nintendo Switch [98973368] = 'XSXS', -- Xbox Series X and Series S [63184502] = 'PS5' } -- Translation table for converting system aliases to QIDs local systemIDs = { ['PS1'] = 10677, ['PS2'] = 10680, ['PS3'] = 10683, ['PS4'] = 5014725, ['PC'] = 16338, ['WII'] = 8079, ['WIIU'] = 56942, ['XBOX'] = 132020, ['X360'] = 48263, ['XONE'] = 13361286, ['3DS'] = 203597, ['PSV'] = 188808, ['DS'] = 170323, ['NDS'] = 170323, ['PSP'] = 170325, ['IOS'] = 48493, ['AND'] = 94, ['GB'] = 186437, ['GBA'] = 188642, ['GBC'] = 203992, ['DC'] = 184198, ['SAT'] = 200912, ['NES'] = 172742, ['SNES'] = 183259, ['N64'] = 184839, ['GC'] = 182172, ['NGC'] = 182172, ['NS'] = 19610114, ['XSXS'] = 98973368, ['PS5'] = 63184502 } -- List of accepted aggregator arguments and their related QID. local aggregatorAliases = { [150248] = 'MC', [40160] = 'GR', [21039459] = 'OC' } -- List of accepted aggregator arguments and their related QID. local aggregatorIDs = { ['MC'] = 150248, ['GR'] = 40160, ['OC'] = 21039459 } -- List of accepted reviewer arguments and their related QID. local reviewerAliases = { [591573] = 'FAM', [207708] = 'IGN' } -- List of accepted reviewer arguments and their related QID. local reviewerIDs = { ['FAM'] = 591573, ['IGN'] = 207708 } local function sortByPlatform(a,b) local platformA = ""; local platformB = ""; if(a['qualifiers']['P400'] ~= nil and a['qualifiers']['P400'][1] ~= nil) then platformA = p.getSystemAlias(a['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); if(platformA == nil) then platformA = mw.wikibase.label('Q'..a['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); end; end; if(b['qualifiers']['P400'] ~= nil and b['qualifiers']['P400'][1] ~= nil) then platformB = p.getSystemAlias(b['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); if(platformB == nil) then platformB = mw.wikibase.label('Q'..b['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); end; end; return platformA < platformB end; local function buildCite(reference) local referenceUrl = nil; local cite = nil; if(reference['snaks']['P854'] ~= nil and reference['snaks']['P854'][1] ~= nil) then referenceUrl = reference['snaks']['P854'][1]['datavalue']['value']; end; if(referenceUrl ~= nil and referenceUrl ~= "") then cite = "{{cite web|url="..referenceUrl; local pubdate = nil; local accessdate = nil; local publisher = nil; local work = nil; local title = nil; local archiveUrl = nil; local archiveDate = nil; local authors = {}; if(reference['snaks']['P577'] ~= nil and reference['snaks']['P577'][1] ~= nil) then pubdate = reference['snaks']['P577'][1]['datavalue']['value']['time']; end; if(reference['snaks']['P813'] ~= nil and reference['snaks']['P813'][1] ~= nil) then accessdate = reference['snaks']['P813'][1]['datavalue']['value']['time']; end; if(reference['snaks']['P123'] ~= nil and reference['snaks']['P123'][1] ~= nil) then publisher = mw.wikibase.label('Q'..reference['snaks']['P123'][1]['datavalue']['value']['numeric-id']); end; if(reference['snaks']['P1433'] ~= nil and reference['snaks']['P1433'][1] ~= nil) then work = mw.wikibase.label('Q'..reference['snaks']['P1433'][1]['datavalue']['value']['numeric-id']); end; if(reference['snaks']['P1476'] ~= nil and reference['snaks']['P1476'][1] ~= nil) then title = reference['snaks']['P1476'][1]['datavalue']['value']['text']; end; if(reference['snaks']['P1065'] ~= nil and reference['snaks']['P1065'][1] ~= nil) then archiveUrl = reference['snaks']['P1065'][1]['datavalue']['value']; end; if(reference['snaks']['P2960'] ~= nil and reference['snaks']['P2960'][1] ~= nil) then archiveDate = reference['snaks']['P2960'][1]['datavalue']['value']['time']; end; if(reference['snaks']['P50'] ~= nil and #reference['snaks']['P50'] > 0) then for i,authorDat in pairs(reference['snaks']['P50']) do local authorQid = 'Q'..authorDat['datavalue']['value']['numeric-id']; if(entities[authorQid] == nil) then entities[authorQid] = mw.wikibase.getEntity(authorQid); end; local author = {}; author['fullname'] = mw.wikibase.label(authorQid); -- Default to label author['first'] = nil; author['last'] = nil; if(entities[authorQid]['claims']['P735'] ~= nil and entities[authorQid]['claims']['P735'][1] ~= nil) then author['first'] = mw.wikibase.label('Q'..entities[authorQid]['claims']['P735'][1]['mainsnak']['datavalue']['value']['numeric-id']); end; if(entities[authorQid]['claims']['P734'] ~= nil and entities[authorQid]['claims']['P734'][1] ~= nil) then author['last'] = mw.wikibase.label('Q'..entities[authorQid]['claims']['P734'][1]['mainsnak']['datavalue']['value']['numeric-id']); end; table.insert(authors, author); end; end; if(title ~= nil and title ~= "") then cite = cite .. "|title="..title; end; if(publisher ~= nil and publisher ~= "") then cite = cite .. "|publisher="..publisher; end; if(work ~= nil and work ~= "") then cite = cite .. "|work="..work; end; if(pubdate ~= nil and pubdate ~= "") then local pubdateText = Date(pubdate):text(df); cite = cite .. "|date="..pubdateText; end; if(accessdate ~= nil and accessdate ~= "") then local accessdateText = Date(accessdate):text(df); cite = cite .. "|accessdate="..accessdateText; end; if(archiveUrl ~= nil and archiveUrl ~= "" and archiveDate ~= nil and archiveDate ~= "") then local archivedateText = Date(archiveDate):text(df); cite = cite .. "|archiveurl="..archiveUrl; cite = cite .. "|archivedate="..archivedateText; end; if(#authors > 0) then for i,author in pairs(authors) do if(author['first'] ~= nil and author['last'] ~= nil and author['first'] ~= "" and author['last'] ~= "") then if(#authors == 1) then cite = cite .."|last="..author['last'].."|first="..author['first']; else cite = cite .."|last"..i.."="..author['last'].."|first"..i.."="..author['first']; end; else if(#authors == 1) then cite = cite .."|author="..author['fullname']; else cite = cite .."|author"..i.."="..author['fullname']; end; end; end; end; cite = cite..'}}'; end; return cite; end; local function printReviewRow(frame, reviewscore) local score = nil; if(reviewscore['mainsnak']['datavalue'] ~= nil and reviewscore['mainsnak']['datavalue']['value'] ~= nil) then score = reviewscore['mainsnak']['datavalue']['value']; else return ""; end; local ret = "" local system = nil; local reference = nil; if(reviewscore['qualifiers']['P400'] ~= nil and reviewscore['qualifiers']['P400'][1] ~= nil) then system = p.getSystemAlias(reviewscore['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); end if(system ~= nil and system ~= "" and showSystem) then if(systemFormat == "para") then ret = ret.."("..system..") "; else ret = ret..system..": "; end; end; ret = ret..score; if(reviewscore['references'] ~= nil and reviewscore['references'][1] ~= nil and genRefs) then local cite = buildCite(reviewscore['references'][1]); if(cite ~= nil) then local scoreBy = p.getAggregatorAlias(reviewscore['qualifiers']['P447'][1]['datavalue']['value']['numeric-id']); if(scoreBy == nil) then scoreBy = p.getReviewerAlias(reviewscore['qualifiers']['P447'][1]['datavalue']['value']['numeric-id']); end; local name = entity:getLabel()..'-'..scoreBy; if(system ~= nil and system ~= "") then name = name..system; end; cite = frame:extensionTag{ name = "ref", args = {name=name}, content=cite }; ret = ret..cite; end; end; return ret.."<br />"; end function p.getSystemAlias(numericId) return systemAliases[numericId]; end function p.getSystemID(system) return systemIDs[system]; end function p.getAggregatorAlias(numericId) return aggregatorAliases[numericId]; end function p.getAggregatorID(system) return aggregatorIDs[system]; end function p.getReviewerAlias(numericId) return reviewerAliases[numericId]; end function p.getReviewerID(system) return reviewerIDs[system]; end function p.setReviewer(iReviewer) -- No reviewer, stop. Must have reviewer at least. if(iReviewer == nil or iReviewer == "") then return "Missing reviewer"; end; -- See if supplied reviewer is in the aggregator table. iReviewer = string.upper(iReviewer) reviewer = p.getAggregatorID(iReviewer); if(reviewer == nil or reviewer == "") then -- No? Maybe in the reviewer table. reviewer = p.getReviewerID(iReviewer); if(reviewer == nil or reviewer == "") then return "Invalid reviewer"; end; end; return nil; end; function p.setDateFormat(iDf) -- Check for a date format parameter. Default to mdy if missing. if(iDf ~= nil and iDf ~= "") then df = string.lower(iDf); end; end; function p.setSystemFormat(iSf) if(iSf ~= nil and iSf ~= "") then systemFormat = string.lower(iSf); end; end; function p.setUpdateLinkStyle(iStyle) if(iStyle ~= nil and iStyle ~= "") then updateLinkStyle = string.lower(iStyle); end; end; function p.setGame(iGame) -- Check for a game parameter. If missing, default to current article. if(iGame ~= nil and iGame ~= "") then if(entities[iGame] == nil and mw.wikibase ~= nil) then entities[iGame] = mw.wikibase.getEntity(iGame); end; entity = entities[iGame] else -- Need to research if we can determine the entity's ID before retrieving it. if(mw.wikibase ~= nil) then entity = mw.wikibase.getEntity(); if(entity ~= nil) then entities[entity['id']] = entity; end; end; end; if(entity == nil) then return "No matching wikidata entity found"; end; return nil; end; function p.setSystem(iSystem) -- Check for system parameter, and resolve it's QID if possible. if(iSystem ~= nil and iSystem ~= "") then system = string.upper(iSystem); systemId = p.getSystemID(system); elseif(not showSystem) then -- If no system was specified, force showSystem on. showSystem = true; end; end; function p.setGenerateReferences(iGenRefs) -- Reference suppression. if(iGenRefs ~= nil and iGenRefs ~= "") then genRefs = yesno(iGenRefs, true); end; end; function p.setShowSystem(iShowSystem) -- Suppression of system aliases in front of score, i.e. (XBOX) xx/100. if(iShowSystem ~= nil and iShowSystem ~= "") then showSystem = yesno(iShowSystem, false); end; if(system == nil or system == '') then -- If no system was specified, force showSystem on. showSystem = true; end; end; function p.setShowUpdateLink(iShowUpdateLink) -- Suppression of update link to Wikidata at the end of the score, i.e. (XBOX) xx/100[+]. if(iShowUpdateLink ~= nil and iShowUpdateLink ~= "") then showUpdateLink = yesno(iShowUpdateLink, false); end; end; function p.getUpdateLink() if(updateLinkStyle == "pen") then return "[[File:Blue pencil.svg|frameless|text-top|10px|alt=Edit this on Wikidata|link=https://www.wikidata.org/wiki/"..entity['id'].."?uselang="..mw.language.getContentLanguage().code.."#P444|Edit this on Wikidata]]"; elseif(updateLinkStyle == "noSub") then return '[[d:'..entity['id']..'#P444|&#91;±&#93;]]'; elseif(updateLinkStyle == "text and pen") then return '<span style="position: relative;"><span style="position: absolute; right: 0;">[[File:Blue pencil.svg|10px|baseline|link=|alt=]]</span>[[d:'..entity['id']..'#P444|<span style="position: relative; padding-right: 14px;">Edit on Wikidata</span>]]</span>' end; return '<sub>[[d:'..entity['id']..'#P444|&#91;±&#93;]]</sub>'; end; function p.getSitelink() return mw.wikibase.sitelink(entity['id']); end; function p.getLabel() return mw.wikibase.label(entity['id']); end; function p.getParts() local ret = {}; -- Loop all of "has Part" for this title local parts = entity['claims']['P527']; if(parts) then for i,part in pairs(parts) do table.insert(ret,"Q"..part['mainsnak']['datavalue']['value']['numeric-id']); end; end; return ret; end; function p.getEarliestPublicationDate() local ret = {}; local pubDates = entity['claims']['P577']; if(pubDates) then for i,pubDate in pairs(pubDates) do if(pubDate['mainsnak']['datavalue']) then local timestamp = pubDate['mainsnak']['datavalue']['value']['time']; local accessdate = Date(timestamp); table.insert(ret,accessdate); end; end; end; if(#ret < 1) then return nil; end; table.sort(ret); return ret[1]; end; function p.printReviewScores(frame) local ret = ""; -- Loop all of "review scores" for this title local reviewscores = entity['claims']['P444']; if(reviewscores) then -- Find reviews that qualify for printing and insert into array. local reviewsToPrint = {} for i,review in pairs(reviewscores) do if(review['qualifiers'] ~= nil) then local scoreBy = nil if(review['qualifiers']['P447'] ~= nil and review['qualifiers']['P447'][1] ~= nil) then scoreBy = review['qualifiers']['P447'][1]['datavalue']['value']['numeric-id']; end; if(scoreBy == reviewer) then -- If template specified a system, we need to check for the specific system and only output that one. if(system == nil or system == "") then -- No system specified, so output each one found. table.insert(reviewsToPrint,review); else -- Get platform if it exists. if(review['qualifiers']['P400'] ~= nil and review['qualifiers']['P400'][1] ~= nil) then -- Try to match based on QID. local reviewSysId = review['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']; if(systemId == reviewSysId) then table.insert(reviewsToPrint,review); else -- If that failed, try to match based on label. local systemName = mw.wikibase.label('Q'..reviewSysId); if(systemName ~= nil and string.upper(systemName) == system) then table.insert(reviewsToPrint,review); end; end; end; end; end; end; end; -- Sort the array by platform label. table.sort(reviewsToPrint, sortByPlatform); -- If a system was not specified, showSystem has defaulted to true. If this title only has one platform and one review, we will turn it off. -- Note: If the title has zero or more platforms defined, we leave showSystem on. We are unable to determine if this is a single-platform game. --if((system == nil or system == "") and #reviewsToPrint == 1 and entity['claims']['P400'] ~= nil and #entity['claims']['P400'] == 1) then -- Simplifying this based on discussion at [Template:Video game reviews]. If there's only one review, don't display system unless explicitly requested. if((system == nil or system == "") and #reviewsToPrint == 1) then showSystem = false; end; -- Print the reviews for i,review in ipairs(reviewsToPrint) do ret = ret .. printReviewRow(frame, review); end; end; if(ret ~= "") then ret = string.sub(ret, 1, -7); elseif(not showUpdateLink) then ret = nil; end; -- Add edit link at end if showUpdateLink is on. if(showUpdateLink) then ret = ret .. p.getUpdateLink(); end; return ret; end; return p 94a235941d390b6a0c1a6827ea88053a159e23e9 Module:ParserFunction 828 520 1048 2023-04-25T03:43:09Z string2>Grufo 0 Add includeonly wikitext text/x-wiki <includeonly>{{#if:{{{_code|}}}|<code>}}{{((}}{{ #switch: {{lc: {{{1|if}}} }} | expr | if | ifeq | iferror | ifexpr | ifexist | rel2abs | switch | time | titleparts = [[mw:Help:Extension:ParserFunctions#.23{{lc:{{{1|if}}}}}|#{{{1|if}}}]] | lc | uc | lcfirst | ucfirst | urlencode | anchorencode | ns = [[meta:Help:Parser function#{{uc:{{{1}}}}}|{{{1}}}]] | localurl | localurle | fullurl | fullurle = [[meta:Help:Parser function#URLs etc.|{{{1}}}]] | language = [[meta:Help:Parser function#.23{{lc:{{{1}}}}}:|#{{{1}}}]] | pagesincategory | pagesincat = [[meta:Help:Parser function#Pages in category|{{{1}}}]] | section | section-x | section-h | lst | lstx | lsth = [[Help:Labeled section transclusion|#{{{1}}}]] | #default = [[H:MW#{{{1}}}|{{{1}}}]] }}{{ #if: {{{2|}}} | {{colon}}{{{2|}}} }}{{ #if: {{{3|}}} | {{ #ifeq: {{{2|}}} | | {{colon}}{{!}} }}{{!}}{{{3}}} }}{{ #if: {{{4|}}} | {{ #ifeq: {{{2|}}} | | {{colon}}{{!}} }}{{ #ifeq: {{{3|}}} | | {{!}} }}{{!}}{{{4}}} }}{{ #if: {{{5|}}} | {{ #ifeq: {{{2|}}} | | {{colon}}{{!}} }}{{ #ifeq: {{{3|}}} | | {{!}} }}{{ #ifeq: {{{4|}}} | | {{!}} }}{{!}}{{{5}}} }}{{))}}{{#if:{{{_code|}}}|</code>}}</includeonly><noinclude> {{documentation}} </noinclude> be6f87cefa60887c80fcf5809a9c95d2050cde70 Template:Documentation subpage 10 329 653 2023-04-29T17:27:17Z wikipedia>Paine Ellsworth 0 m wikitext text/x-wiki <includeonly><!-- -->{{#ifeq:{{lc:{{SUBPAGENAME}}}} |{{{override|doc}}} | <!--(this template has been transcluded on a /doc or /{{{override}}} page)--> </includeonly><!-- -->{{#ifeq:{{{doc-notice|show}}} |show | {{Mbox | type = notice | style = margin-bottom:1.0em; | image = [[File:Edit-copy green.svg|40px|alt=|link=]] | text = {{strong|This is a [[Wikipedia:Template documentation|documentation]] [[Wikipedia:Subpages|subpage]]}} for {{terminate sentence|{{{1|[[:{{SUBJECTSPACE}}:{{BASEPAGENAME}}]]}}}}}<br />It may contain usage information, [[Wikipedia:Categorization|categories]] and other content that is not part of the original {{#if:{{{text2|}}} |{{{text2}}} |{{#if:{{{text1|}}} |{{{text1}}} |{{#ifeq:{{SUBJECTSPACE}} |{{ns:User}} |{{lc:{{SUBJECTSPACE}}}} template page |{{#if:{{SUBJECTSPACE}} |{{lc:{{SUBJECTSPACE}}}} page|article}}}}}}}}. }} }}<!-- -->{{DEFAULTSORT:{{{defaultsort|{{PAGENAME}}}}}}}<!-- -->{{#if:{{{inhibit|}}} |<!--(don't categorize)--> | <includeonly><!-- -->{{#ifexist:{{NAMESPACE}}:{{BASEPAGENAME}} | [[Category:{{#switch:{{SUBJECTSPACE}} |Template=Template |Module=Module |User=User |#default=Wikipedia}} documentation pages]] | [[Category:Documentation subpages without corresponding pages]] }}<!-- --></includeonly> }}<!-- (completing initial #ifeq: at start of template:) --><includeonly> | <!--(this template has not been transcluded on a /doc or /{{{override}}} page)--> }}<!-- --></includeonly><noinclude>{{Documentation}}</noinclude> 41ca90af0945442788a2dbd08c8c54a61a23c057 Module:String2/doc 828 528 1064 2023-05-02T18:03:43Z string2>Aidan9382 0 /* See also */ fix editor syntax highlighting wikitext text/x-wiki {{high-use}} {{module rating|release}} {{Lua|Module:GetParameters}} {{Lmd|String2}} The module '''String2''' contains a number of string manipulation functions that are much less commonly used than those in [[Module:String]]. Because Module:String is cascade-protected (some of its functions are used on the Main Page), it cannot be edited or maintained by template editors, only by admins. While it is true that string-handling functions rarely need maintenance, it is useful to allow that by template editors where possible, so this module may be used by template editors to develop novel functionality. The module contains three case-related calls that convert strings to first letter uppercase, sentence case or title case and two calls that are useful for working with substrings. There are other utility calls that strip leading zeros from padded numbers and transform text so that it is not interpreted as wikitext, and several other calls that solve specific problems for template developers such as finding the position of a piece of text on a given page. The functions are designed with the possibility of working with text returned from Wikidata in mind. However, a call to Wikidata may return empty, so the functions should generally fail gracefully if supplied with a missing or blank input parameter, rather than throwing an error. == Functions == === trim === The trim function simply trims whitespace characters from the start and end of the string. === title === The title function capitalises the first letter of each word in the text, apart from a number of short words recommended by [[s:U.S. Government Printing Office Style Manual/Capitalization Rules|The U.S. Government Printing Office Style Manual]]: {{xt|a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor}}. === sentence === The sentence function finds the first letter and capitalises it, then renders the rest of the text in lower case. It works properly with text containing wiki markup. Compare <code><nowiki>{{#invoke:String2|sentence|[[action game]]}}</nowiki></code> &rarr; {{#invoke:String2|sentence|[[action game]]}} with <code><nowiki>{{ucfirst:{{lc:[[action game]]}}}}</nowiki></code> &rarr; {{ucfirst:{{lc:[[action game]]}}}}. Piped wiki-links are handled as well: * <code><nowiki>{{#invoke:String2|sentence|[[trimix (breathing gas)|trimix]]}}</nowiki></code> &rarr; {{#invoke:String2|sentence|[[trimix (breathing gas)|trimix]]}} So are lists: * <code><nowiki>{{#invoke:String2 |sentence |{{hlist ||[[apples]] |[[pears]] |[[oranges]]}}}}</nowiki></code> → {{#invoke:String2 |sentence |{{hlist |[[apples]] |[[pears]] |[[oranges]]}}}} === ucfirst === The ucfirst function is similar to sentence; it renders the first alphabetical character in upper case, but leaves the capitalisation of the rest of the text unaltered. This is useful if the text contains proper nouns, but it will not regularise sentences that are ALLCAPS, for example. It also works with text containing piped wiki-links and with html lists. === findlast === * Function findlast finds the last item in a list. * The first unnamed parameter is the list. The list is trimmed of leading and trailing whitespace * The second, optional unnamed parameter is the list separator (default = comma space). The separator is ''not'' trimmed of leading and trailing whitespace (so that leading or trailing spaces can be used). * It returns the whole list if the separator is not found. One potential issue is that using Lua special pattern characters (<code>^$()%.[]*+-?</code>) as the separator will probably cause problems. {| class="wikitable" |+ Examples |- ! scope="col" | Case ! scope="col" | Wikitext ! scope="col" | Output |- | Normal usage || <code><nowiki>{{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291}}</nowiki></code> || {{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291}} |- | Space as separator || <code><nowiki>{{#invoke:String2 |findlast | 5 932 992,532 6,074,702 6,145,291 }}</nowiki></code> || {{#invoke:String2 |findlast | 5 932 992,532 6,074,702 6,145,291 }} |- | One item list || <code><nowiki>{{#invoke:String2 |findlast | 6,074,702 }}</nowiki></code> || {{#invoke:String2 |findlast | 6,074,702 }} |- | Separator not found || <code><nowiki>{{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291 |;}}</nowiki></code> || {{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291 |;}} |- | List missing || <code><nowiki>{{#invoke:String2 |findlast |}}</nowiki></code> || {{#invoke:String2 |findlast |}} |} === split === The ''split'' function splits text at boundaries specified by separator and returns the chunk for the index idx (starting at 1). It can use positional parameters or named parameters (but these should not be mixed): * <code><nowiki>{{#invoke:String2 |split |text |separator |index |true/false}}</nowiki></code> * <code><nowiki>{{#invoke:String2 |split |txt=text |sep=separator |idx=index |plain=true/false}}</nowiki></code> Any double quotes (") in the separator parameter are stripped out, which allows spaces and wikitext like <code><nowiki>["[</nowiki></code> to be passed. Use <code>{{tl|!}}</code> for the pipe character <code>|</code>. If the optional plain parameter is set to <code>false / no / 0</code> then separator is treated as a Lua pattern. The default is plain=true, i.e. normal text matching. The index parameter is optional; it defaults to the first chunk of text. The [[Template:Stringsplit]] is a convenience wrapper for the split function. === stripZeros === The stripZeros functions finds the first number in a string of text and strips leading zeros, but retains a zero which is followed by a decimal point. For example: "0940" &rarr; "940"; "Year: 0023" &rarr; "Year: 23"; "00.12" &rarr; "0.12" === nowiki === The nowiki function ensures that a string of text is treated by the MediaWiki software as just a string, not code. It trims leading and trailing whitespace. === val2percent === The val2percent functions scans through a string, passed as either the first unnamed parameter or |txt=, and converts each number it finds into a percentage, then returns the resulting string. === one2a === The one2a function scans through a string, passed as either the first unnamed parameter or |txt=, and converts each occurrence of 'one ' into either 'a ' or 'an ', then returns the resultant string. The [[Template:One2a]] is a convenience wrapper for the one2a function. === findpagetext === The findpagetext function returns the position of a piece of text in the wikitext source of a page. It takes up to four parameters: * First positional parameter or |text is the text to be searched for. * Optional parameter |title is the page title, defaults to the current page. * Optional parameter |plain is either true for a plain search (default), or false for a [[Extension:Scribunto/Lua reference manual #Patterns|Lua pattern]] search. * Optional parameter |nomatch is the value returned when no match is found; default is nothing. ; Examples : <code><nowiki>{{#invoke:String2 |findpagetext |text=Youghiogheny}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=Youghiogheny}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=Youghiogheny |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=Youghiogheny |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=Youghiogheny |title=Boston Bridge |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=Youghiogheny |title=Boston Bridge |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=river |title=Boston Bridge |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=river |title=Boston Bridge |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=[Rr]iver |title=Boston Bridge |plain=false |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=[Rr]iver |title=Boston Bridge |plain=false |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=%[%[ |title=Boston Bridge |plain=f |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=%[%[ |title=Boston Bridge |plain=f |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=%{%{[Cc]oord |title=Boston Bridge |plain=f |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=%{%{coord |title=Boston Bridge |plain=f |nomatch=not found}} The search is case-sensitive, so Lua pattern matching is needed to find <code>river</code> or <code>River</code>. The last example finds <code><nowiki>{{coord</nowiki></code> and <code><nowiki>{{Coord</nowiki></code>. The penultimate example finds a wiki-link. The [[Template:Findpagetext]] is a convenience wrapper for this function. === strip === The strip function strips the first positional parameter of the characters or pattern supplied in the second positional parameter. === matchAny === {{for|a function to replace multiple patterns|Module:MultiReplace}} The matchAny function returns the index of the first positional parameter to match the ''source'' parameter. If the ''plain'' parameter is set to false (default true) then the search strings are Lua patterns. This can usefully be put in a switch statement to pick a switch case based on which pattern a string matches. Returns the empty string if nothing matches, for use in {{pf|if}}. <code>{{((}}#invoke:String2|matchAny|123|abc|source=abc 124}}</code> returns 2. === hyphen2dash === Extracted hyphen_to_dash() function from [[Special:Permalink/1017669505|Module:Citation/CS1]]. Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are returned unmodified. These forms are modified: * letter - letter (A - B) * digit - digit (4-5) * digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) * letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) * digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d) Any other forms are returned unmodified. The input string may be a comma- or semicolon-separated list. Semicolons are converted to commas. <code><nowiki>{{</nowiki>#invoke:String2|hyphen2dash|1=1-2<nowiki>}}</nowiki></code> returns {{#invoke:String2|hyphen2dash|1=1-2}}. <code><nowiki>{{</nowiki>#invoke:String2|hyphen2dash|1=1-2; 4–10<nowiki>}}</nowiki></code> returns {{#invoke:String2|hyphen2dash|1=1-2; 4–10}}. [[Help:Citation Style 1#Accept-this-as-written markup|Accept-this-as-written markup]] is supported, e.g. <code><nowiki>{{</nowiki>#invoke:String2|hyphen2dash|1=((1-2)); 4–10<nowiki>}}</nowiki></code> returns {{#invoke:String2|hyphen2dash|1=((1-2)); 4–10}}. By default, a normal space is inserted after the separating comma in lists. An optional second parameter allows to change this to a different character (i.e. a thin space or hair space). ===startswith=== A startswith function similar to {{ml|string|endswith}}. Both parameters are required, although they can be blank. Leading and trailing whitespace ''is'' counted, use named parameters to avoid this if required. Outputs "yes" for true and blank for false so may be passed directly to #if. {{markup| <nowiki>{{#invoke:string2|startswith|search|se}}</nowiki>|{{#invoke:string2|startswith|search|se}}| <nowiki>{{#invoke:string2|startswith|search|ch}}</nowiki>|{{#invoke:string2|startswith|search|ch}}}}<!-- Template:Mra --> == Usage == * <code><nowiki>{{#invoke:String2 | sentence |…}}</nowiki></code> - Capitalizes the first character and shifts the rest to lowercase ** Although similar to [[Help:Magic_words#Formatting|magic words]]' <code><nowiki>{{ucfirst:}}</nowiki></code> function, this call works even with piped wiki-links because it searches beyond leading brackets and other non-alphanumeric characters. ** It now also recognises when it has an html list passed to it and capitalises the first alphabetic letter beyond the list item markup ({{tag|li|o}}) and any piped links that may be there. * <code><nowiki>{{#invoke:String2 | ucfirst |…}}</nowiki></code> - Capitalizes the first alphabetic character and leaves the rest unaltered ** Works with piped wiki-links and html lists * <code><nowiki>{{#invoke:String2 | title |…}}</nowiki></code> - Capitalizes all words, except for <code>a</code>, <code>an</code>, <code>the</code>, <code>at</code>, <code>by</code>, <code>for</code>, <code>in</code>, <code>of</code>, <code>on</code>, <code>to</code>, <code>up</code>, <code>and</code>, <code>as</code>, <code>but</code>, <code>or</code>, and <code>nor</code>. * <code><nowiki>{{#invoke:String2 | stripZeros |…}}</nowiki></code> - Removes leading padding zeros from the first number it finds in the string * <code><nowiki>{{#invoke:String2 | title |…}}</nowiki></code> - Renders the string as plain text without wikicode === Parameters === These functions take one unnamed parameter comprising (or invoking as a string) the text to be manipulated: * title * sentence * ucfirst == Examples == {| class="wikitable" ! scope="col" | Input ! scope="col" | Output |- | <nowiki>{{#invoke:String2| ucfirst | abcd }}</nowiki> | {{#invoke:String2| ucfirst | abcd }} |- | <nowiki>{{#invoke:String2| ucfirst | abCD }}</nowiki> | {{#invoke:String2| ucfirst | abCD }} |- | <nowiki>{{#invoke:String2| ucfirst | ABcd }}</nowiki> | {{#invoke:String2| ucfirst | ABcd }} |- | <nowiki>{{#invoke:String2| ucfirst | ABCD }}</nowiki> | {{#invoke:String2| ucfirst | ABCD }} |- | <nowiki>{{#invoke:String2| ucfirst | 123abcd }}</nowiki> | {{#invoke:String2| ucfirst | 123abcd }} |- | <nowiki>{{#invoke:String2| ucfirst | }}</nowiki> | {{#invoke:String2| ucfirst | }} |- | <nowiki>{{#invoke:String2| ucfirst | human X chromosome }}</nowiki> | {{#invoke:String2| ucfirst | human X chromosome}} |- | <nowiki>{{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue</nowiki><br /><nowiki>| P136 |fetchwikidata=ALL |onlysourced=no |qid=Q1396889}} }}</nowiki> | {{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue |P136 |fetchwikidata=ALL |onlysourced=no |qid=Q1396889}} }} |- | <nowiki>{{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue</nowiki><br /><nowiki>| P106 |fetchwikidata=ALL |list=hlist |qid=Q453196}} }}</nowiki> | {{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue |P106 |fetchwikidata=ALL |list=hlist |qid=Q453196}} }} |- | &nbsp; | |- | <nowiki>{{#invoke:String2| sentence | abcd }}</nowiki> | {{#invoke:String2| sentence | abcd }} |- | <nowiki>{{#invoke:String2| sentence | abCD }}</nowiki> | {{#invoke:String2| sentence | abCD }} |- | <nowiki>{{#invoke:String2| sentence | ABcd }}</nowiki> | {{#invoke:String2| sentence | ABcd }} |- | <nowiki>{{#invoke:String2| sentence | ABCD }}</nowiki> | {{#invoke:String2| sentence | ABCD }} |- | <nowiki>{{#invoke:String2| sentence | [[action game]] }}</nowiki> | {{#invoke:String2| sentence | [[action game]] }} |- | <nowiki>{{#invoke:String2| sentence | [[trimix (breathing gas)|trimix]] }}</nowiki> | {{#invoke:String2| sentence | [[trimix (breathing gas)|trimix]] }} |- | <nowiki>{{#invoke:String2| sentence | }}</nowiki> | {{#invoke:String2| sentence | }} |- | &nbsp; | |- | <nowiki>{{#invoke:String2| title | abcd }}</nowiki> | {{#invoke:String2| title | abcd }} |- | <nowiki>{{#invoke:String2| title | abCD }}</nowiki> | {{#invoke:String2| title | abCD }} |- | <nowiki>{{#invoke:String2| title | ABcd }}</nowiki> | {{#invoke:String2| title | ABcd }} |- | <nowiki>{{#invoke:String2| title | ABCD }}</nowiki> | {{#invoke:String2| title | ABCD }} |- | <nowiki>{{#invoke:String2| title | }}</nowiki> | {{#invoke:String2| title | }} |- | <nowiki>{{#invoke:String2| title | the vitamins are in my fresh california raisins}}</nowiki> | {{#invoke:String2| title | the vitamins are in my fresh california raisins}} |- |} === String split === [[Template:String split]] is a convenience wrapper for the split function. * <code><nowiki>{{String split |This is a piece of text to be split |" "}}</nowiki></code> → {{String split |This is a piece of text to be split |" "}} * <code><nowiki>{{String split |This is a piece of text to be split |" "| 4}}</nowiki></code> → {{String split |This is a piece of text to be split |" "| 4}} * <code><nowiki>{{String split |This is a piece of text to be split |x| 2}}</nowiki></code> → {{String split |This is a piece of text to be split |x| 2}} Modules may return strings with | as separators like this: <code><nowiki>{{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }}</nowiki></code> → {{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }} * <code><nowiki>{{String split |{{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }}|{{!}}| 2}}</nowiki></code> → {{String split |{{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }}|{{!}}| 2}} Lua patterns can allow splitting at classes of characters such as punctuation: * <code><nowiki>{{String split |Apples, pears, oranges; Cats, dogs|"%p"| 2 |false}}</nowiki></code> → {{String split |Apples, pears, oranges; Cats, dogs|"%p"| 2 |false}} * <code><nowiki>{{String split |Apples, pears, oranges; Cats, dogs|"%p"| 4 |false}}</nowiki></code> → {{String split |Apples, pears, oranges; Cats, dogs|"%p"| 4 |false}} Or split on anything that isn't a letter (no is treated as false): * <code><nowiki>{{String split |Apples pears oranges; Cats dogs|"%A+"| 4 |no}}</nowiki></code> → {{String split |Apples pears oranges; Cats dogs|"%A+"| 4 |no}} Named parameters force the trimming of leading and trailing spaces in the parameters and are generally clearer when used: * <code><nowiki>{{String split | txt=Apples pears oranges; Cats dogs | sep="%A+" | idx=3 | plain=false }}</nowiki></code> → {{String split | txt=Apples pears oranges; Cats dogs | sep="%A+" | idx=3 | plain=false }} === One2a === [[Template:One2a]] is a convenience wrapper for the one2a function. Capitalisation is kept. Aimed for usage with {{tl|Convert}}. * <code><nowiki>{{one2a |One foot. One mile. One kilometer. One inch.One amp. one foot. one mile. one inch. Alone at last. Onely the lonely. ONE ounce. One monkey.}}</nowiki></code> → :{{one2a |One foot. One mile. One kilometer. One inch.One amp. one foot. one mile. one inch. Alone at last. Onely the lonely. ONE ounce. One monkey.}} * <code><nowiki>{{convert|1|ft|spell=on}}</nowiki></code> → {{convert|1|ft|spell=on}} * <code><nowiki>{{one2a|{{convert|1|ft|spell=on}}}}</nowiki></code> → {{one2a|{{convert|1|ft|spell=on}}}} * <code><nowiki>{{convert|2.54|cm|0|disp=out|spell=on}}</nowiki></code> → {{convert|2.54|cm|0|disp=out|spell=on}} * <code><nowiki>{{one2a|{{convert|2.54|cm|0|disp=out|spell=on}}}}</nowiki></code> → {{one2a|{{convert|2.54|cm|0|disp=out|spell=on}}}} == See also == [[Module:String]] for the following functions: * len * sub * sublength * match * pos * str_find * find * replace * rep Templates and modules related to capitalization {{Case templates see also}} Templates that implement {{tag|nowiki|o}} * {{tl|nowiki}} * {{tl|nowiki2}} <includeonly>{{Sandbox other|| [[Category:Modules that manipulate strings|*]] }}</includeonly> 16cb4393067392cf526ac4ea120a04fca4da7ea0 Module:Protection banner/config 828 358 710 2023-05-08T11:41:01Z wikipedia>Fayenatic london 0 Update categories from "fully-protected" to "fully protected", removing hyphen, per valid request at [[WP:CFDS]] Scribunto text/plain -- This module provides configuration data for [[Module:Protection banner]]. return { -------------------------------------------------------------------------------- -- -- BANNER DATA -- -------------------------------------------------------------------------------- --[[ -- Banner data consists of six fields: -- * text - the main protection text that appears at the top of protection -- banners. -- * explanation - the text that appears below the main protection text, used -- to explain the details of the protection. -- * tooltip - the tooltip text you see when you move the mouse over a small -- padlock icon. -- * link - the page that the small padlock icon links to. -- * alt - the alt text for the small padlock icon. This is also used as tooltip -- text for the large protection banners. -- * image - the padlock image used in both protection banners and small padlock -- icons. -- -- The module checks in three separate tables to find a value for each field. -- First it checks the banners table, which has values specific to the reason -- for the page being protected. Then the module checks the defaultBanners -- table, which has values specific to each protection level. Finally, the -- module checks the masterBanner table, which holds data for protection -- templates to use if no data has been found in the previous two tables. -- -- The values in the banner data can take parameters. These are specified -- using ${TEXTLIKETHIS} (a dollar sign preceding a parameter name -- enclosed in curly braces). -- -- Available parameters: -- -- ${CURRENTVERSION} - a link to the page history or the move log, with the -- display message "current-version-edit-display" or -- "current-version-move-display". -- -- ${EDITREQUEST} - a link to create an edit request for the current page. -- -- ${EXPLANATIONBLURB} - an explanation blurb, e.g. "Please discuss any changes -- on the talk page; you may submit a request to ask an administrator to make -- an edit if it is minor or supported by consensus." -- -- ${IMAGELINK} - a link to set the image to, depending on the protection -- action and protection level. -- -- ${INTROBLURB} - the PROTECTIONBLURB parameter, plus the expiry if an expiry -- is set. E.g. "Editing of this page by new or unregistered users is currently -- disabled until dd Month YYYY." -- -- ${INTROFRAGMENT} - the same as ${INTROBLURB}, but without final punctuation -- so that it can be used in run-on sentences. -- -- ${PAGETYPE} - the type of the page, e.g. "article" or "template". -- Defined in the cfg.pagetypes table. -- -- ${PROTECTIONBLURB} - a blurb explaining the protection level of the page, e.g. -- "Editing of this page by new or unregistered users is currently disabled" -- -- ${PROTECTIONDATE} - the protection date, if it has been supplied to the -- template. -- -- ${PROTECTIONLEVEL} - the protection level, e.g. "fully protected" or -- "semi-protected". -- -- ${PROTECTIONLOG} - a link to the protection log or the pending changes log, -- depending on the protection action. -- -- ${TALKPAGE} - a link to the talk page. If a section is specified, links -- straight to that talk page section. -- -- ${TOOLTIPBLURB} - uses the PAGETYPE, PROTECTIONTYPE and EXPIRY parameters to -- create a blurb like "This template is semi-protected", or "This article is -- move-protected until DD Month YYYY". -- -- ${VANDAL} - links for the specified username (or the root page name) -- using Module:Vandal-m. -- -- Functions -- -- For advanced users, it is possible to use Lua functions instead of strings -- in the banner config tables. Using functions gives flexibility that is not -- possible just by using parameters. Functions take two arguments, the -- protection object and the template arguments, and they must output a string. -- -- For example: -- -- text = function (protectionObj, args) -- if protectionObj.level == 'autoconfirmed' then -- return 'foo' -- else -- return 'bar' -- end -- end -- -- Some protection object properties and methods that may be useful: -- protectionObj.action - the protection action -- protectionObj.level - the protection level -- protectionObj.reason - the protection reason -- protectionObj.expiry - the expiry. Nil if unset, the string "indef" if set -- to indefinite, and the protection time in unix time if temporary. -- protectionObj.protectionDate - the protection date in unix time, or nil if -- unspecified. -- protectionObj.bannerConfig - the banner config found by the module. Beware -- of editing the config field used by the function, as it could create an -- infinite loop. -- protectionObj:isProtected - returns a boolean showing whether the page is -- protected. -- protectionObj:isTemporary - returns a boolean showing whether the expiry is -- temporary. -- protectionObj:isIncorrect - returns a boolean showing whether the protection -- template is incorrect. --]] -- The master banner data, used if no values have been found in banners or -- defaultBanners. masterBanner = { text = '${INTROBLURB}', explanation = '${EXPLANATIONBLURB}', tooltip = '${TOOLTIPBLURB}', link = '${IMAGELINK}', alt = 'Page ${PROTECTIONLEVEL}' }, -- The default banner data. This holds banner data for different protection -- levels. -- *required* - this table needs edit, move, autoreview and upload subtables. defaultBanners = { edit = {}, move = {}, autoreview = { default = { alt = 'Page protected with pending changes', tooltip = 'All edits by unregistered and new users are subject to review prior to becoming visible to unregistered users', image = 'Pending-protection-shackle.svg' } }, upload = {} }, -- The banner data. This holds banner data for different protection reasons. -- In fact, the reasons specified in this table control which reasons are -- valid inputs to the first positional parameter. -- -- There is also a non-standard "description" field that can be used for items -- in this table. This is a description of the protection reason for use in the -- module documentation. -- -- *required* - this table needs edit, move, autoreview and upload subtables. banners = { edit = { blp = { description = 'For pages protected to promote compliance with the' .. ' [[Wikipedia:Biographies of living persons' .. '|biographies of living persons]] policy', text = '${INTROFRAGMENT} to promote compliance with' .. ' [[Wikipedia:Biographies of living persons' .. "|Wikipedia's&nbsp;policy on&nbsp;the&nbsp;biographies" .. ' of&nbsp;living&nbsp;people]].', tooltip = '${TOOLTIPFRAGMENT} to promote compliance with the policy on' .. ' biographies of living persons', }, dmca = { description = 'For pages protected by the Wikimedia Foundation' .. ' due to [[Digital Millennium Copyright Act]] takedown requests', explanation = function (protectionObj, args) local ret = 'Pursuant to a rights owner notice under the Digital' .. ' Millennium Copyright Act (DMCA) regarding some content' .. ' in this article, the Wikimedia Foundation acted under' .. ' applicable law and took down and restricted the content' .. ' in question.' if args.notice then ret = ret .. ' A copy of the received notice can be found here: ' .. args.notice .. '.' end ret = ret .. ' For more information, including websites discussing' .. ' how to file a counter-notice, please see' .. " [[Wikipedia:Office actions]] and the article's ${TALKPAGE}." .. "'''Do not remove this template from the article until the" .. " restrictions are withdrawn'''." return ret end, image = 'Office-protection-shackle.svg', }, dispute = { description = 'For pages protected due to editing disputes', text = function (protectionObj, args) -- Find the value of "disputes". local display = 'disputes' local disputes if args.section then disputes = string.format( '[[%s:%s#%s|%s]]', mw.site.namespaces[protectionObj.title.namespace].talk.name, protectionObj.title.text, args.section, display ) else disputes = display end -- Make the blurb, depending on the expiry. local msg if type(protectionObj.expiry) == 'number' then msg = '${INTROFRAGMENT} or until editing %s have been resolved.' else msg = '${INTROFRAGMENT} until editing %s have been resolved.' end return string.format(msg, disputes) end, explanation = "This protection is '''not''' an endorsement of the" .. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}', tooltip = '${TOOLTIPFRAGMENT} due to editing disputes', }, ecp = { description = 'For articles in topic areas authorized by' .. ' [[Wikipedia:Arbitration Committee|ArbCom]] or' .. ' meets the criteria for community use', tooltip = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}', alt = 'Extended-protected ${PAGETYPE}', }, mainpage = { description = 'For pages protected for being displayed on the [[Main Page]]', text = 'This file is currently' .. ' [[Wikipedia:This page is protected|protected]] from' .. ' editing because it is currently or will soon be displayed' .. ' on the [[Main Page]].', explanation = 'Images on the Main Page are protected due to their high' .. ' visibility. Please discuss any necessary changes on the ${TALKPAGE}.' .. '<br /><span style="font-size:90%;">' .. "'''Administrators:''' Once this image is definitely off the Main Page," .. ' please unprotect this file, or reduce to semi-protection,' .. ' as appropriate.</span>', }, office = { description = 'For pages protected by the Wikimedia Foundation', text = function (protectionObj, args) local ret = 'This ${PAGETYPE} is currently under the' .. ' scrutiny of the' .. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]' .. ' and is protected.' if protectionObj.protectionDate then ret = ret .. ' It has been protected since ${PROTECTIONDATE}.' end return ret end, explanation = "If you can edit this page, please discuss all changes and" .. " additions on the ${TALKPAGE} first. '''Do not remove protection from this" .. " page unless you are authorized by the Wikimedia Foundation to do" .. " so.'''", image = 'Office-protection-shackle.svg', }, reset = { description = 'For pages protected by the Wikimedia Foundation and' .. ' "reset" to a bare-bones version', text = 'This ${PAGETYPE} is currently under the' .. ' scrutiny of the' .. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]' .. ' and is protected.', explanation = function (protectionObj, args) local ret = '' if protectionObj.protectionDate then ret = ret .. 'On ${PROTECTIONDATE} this ${PAGETYPE} was' else ret = ret .. 'This ${PAGETYPE} has been' end ret = ret .. ' reduced to a' .. ' simplified, "bare bones" version so that it may be completely' .. ' rewritten to ensure it meets the policies of' .. ' [[WP:NPOV|Neutral Point of View]] and [[WP:V|Verifiability]].' .. ' Standard Wikipedia policies will apply to its rewriting—which' .. ' will eventually be open to all editors—and will be strictly' .. ' enforced. The ${PAGETYPE} has been ${PROTECTIONLEVEL} while' .. ' it is being rebuilt.\n\n' .. 'Any insertion of material directly from' .. ' pre-protection revisions of the ${PAGETYPE} will be removed, as' .. ' will any material added to the ${PAGETYPE} that is not properly' .. ' sourced. The associated talk page(s) were also cleared on the' .. " same date.\n\n" .. "If you can edit this page, please discuss all changes and" .. " additions on the ${TALKPAGE} first. '''Do not override" .. " this action, and do not remove protection from this page," .. " unless you are authorized by the Wikimedia Foundation" .. " to do so. No editor may remove this notice.'''" return ret end, image = 'Office-protection-shackle.svg', }, sock = { description = 'For pages protected due to' .. ' [[Wikipedia:Sock puppetry|sock puppetry]]', text = '${INTROFRAGMENT} to prevent [[Wikipedia:Sock puppetry|sock puppets]] of' .. ' [[Wikipedia:Blocking policy|blocked]] or' .. ' [[Wikipedia:Banning policy|banned users]]' .. ' from editing it.', tooltip = '${TOOLTIPFRAGMENT} to prevent sock puppets of blocked or banned users from' .. ' editing it', }, template = { description = 'For [[Wikipedia:High-risk templates|high-risk]]' .. ' templates and Lua modules', text = 'This is a permanently [[Help:Protection|protected]] ${PAGETYPE},' .. ' as it is [[Wikipedia:High-risk templates|high-risk]].', explanation = 'Please discuss any changes on the ${TALKPAGE}; you may' .. ' ${EDITREQUEST} to ask an' .. ' [[Wikipedia:Administrators|administrator]] or' .. ' [[Wikipedia:Template editor|template editor]] to make an edit if' .. ' it is [[Help:Minor edit#When to mark an edit as a minor edit' .. '|uncontroversial]] or supported by' .. ' [[Wikipedia:Consensus|consensus]]. You can also' .. ' [[Wikipedia:Requests for page protection|request]] that the page be' .. ' unprotected.', tooltip = 'This high-risk ${PAGETYPE} is permanently ${PROTECTIONLEVEL}' .. ' to prevent vandalism', alt = 'Permanently protected ${PAGETYPE}', }, usertalk = { description = 'For pages protected against disruptive edits by a' .. ' particular user', text = '${INTROFRAGMENT} to prevent ${VANDAL} from using it to make disruptive edits,' .. ' such as abusing the' .. ' &#123;&#123;[[Template:unblock|unblock]]&#125;&#125; template.', explanation = 'If you cannot edit this user talk page and you need to' .. ' make a change or leave a message, you can' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for edits to a protected page' .. '|request an edit]],' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]],' .. ' [[Special:Userlogin|log in]],' .. ' or [[Special:UserLogin/signup|create an account]].', }, vandalism = { description = 'For pages protected against' .. ' [[Wikipedia:Vandalism|vandalism]]', text = '${INTROFRAGMENT} due to [[Wikipedia:Vandalism|vandalism]].', explanation = function (protectionObj, args) local ret = '' if protectionObj.level == 'sysop' then ret = ret .. "This protection is '''not''' an endorsement of the" .. ' ${CURRENTVERSION}. ' end return ret .. '${EXPLANATIONBLURB}' end, tooltip = '${TOOLTIPFRAGMENT} due to vandalism', } }, move = { dispute = { description = 'For pages protected against page moves due to' .. ' disputes over the page title', explanation = "This protection is '''not''' an endorsement of the" .. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}', image = 'Move-protection-shackle.svg' }, vandalism = { description = 'For pages protected against' .. ' [[Wikipedia:Vandalism#Page-move vandalism' .. ' |page-move vandalism]]' } }, autoreview = {}, upload = {} }, -------------------------------------------------------------------------------- -- -- GENERAL DATA TABLES -- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- Protection blurbs -------------------------------------------------------------------------------- -- This table produces the protection blurbs available with the -- ${PROTECTIONBLURB} parameter. It is sorted by protection action and -- protection level, and is checked by the module in the following order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level -- 3. "edit" protection action, default protection level -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. protectionBlurbs = { edit = { default = 'This ${PAGETYPE} is currently [[Help:Protection|' .. 'protected]] from editing', autoconfirmed = 'Editing of this ${PAGETYPE} by [[Wikipedia:User access' .. ' levels#New users|new]] or [[Wikipedia:User access levels#Unregistered' .. ' users|unregistered]] users is currently [[Help:Protection|disabled]]', extendedconfirmed = 'This ${PAGETYPE} is currently under extended confirmed protection', }, move = { default = 'This ${PAGETYPE} is currently [[Help:Protection|protected]]' .. ' from [[Help:Moving a page|page moves]]' }, autoreview = { default = 'All edits made to this ${PAGETYPE} by' .. ' [[Wikipedia:User access levels#New users|new]] or' .. ' [[Wikipedia:User access levels#Unregistered users|unregistered]]' .. ' users are currently' .. ' [[Wikipedia:Pending changes|subject to review]]' }, upload = { default = 'Uploading new versions of this ${PAGETYPE} is currently disabled' } }, -------------------------------------------------------------------------------- -- Explanation blurbs -------------------------------------------------------------------------------- -- This table produces the explanation blurbs available with the -- ${EXPLANATIONBLURB} parameter. It is sorted by protection action, -- protection level, and whether the page is a talk page or not. If the page is -- a talk page it will have a talk key of "talk"; otherwise it will have a talk -- key of "subject". The table is checked in the following order: -- 1. page's protection action, page's protection level, page's talk key -- 2. page's protection action, page's protection level, default talk key -- 3. page's protection action, default protection level, page's talk key -- 4. page's protection action, default protection level, default talk key -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. explanationBlurbs = { edit = { autoconfirmed = { subject = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details. If you' .. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can' .. ' ${EDITREQUEST}, discuss changes on the ${TALKPAGE},' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]], [[Special:Userlogin|log in]], or' .. ' [[Special:UserLogin/signup|create an account]].', default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details. If you' .. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]], [[Special:Userlogin|log in]], or' .. ' [[Special:UserLogin/signup|create an account]].', }, extendedconfirmed = { default = 'Extended confirmed protection prevents edits from all unregistered editors' .. ' and registered users with fewer than 30 days tenure and 500 edits.' .. ' The [[Wikipedia:Protection policy#extended|policy on community use]]' .. ' specifies that extended confirmed protection can be applied to combat' .. ' disruption, if semi-protection has proven to be ineffective.' .. ' Extended confirmed protection may also be applied to enforce' .. ' [[Wikipedia:Arbitration Committee|arbitration sanctions]].' .. ' Please discuss any changes on the ${TALKPAGE}; you may' .. ' ${EDITREQUEST} to ask for uncontroversial changes supported by' .. ' [[Wikipedia:Consensus|consensus]].' }, default = { subject = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' Please discuss any changes on the ${TALKPAGE}; you' .. ' may ${EDITREQUEST} to ask an' .. ' [[Wikipedia:Administrators|administrator]] to make an edit if it' .. ' is [[Help:Minor edit#When to mark an edit as a minor edit' .. '|uncontroversial]] or supported by [[Wikipedia:Consensus' .. '|consensus]]. You may also [[Wikipedia:Requests for' .. ' page protection#Current requests for reduction in protection level' .. '|request]] that this page be unprotected.', default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' You may [[Wikipedia:Requests for page' .. ' protection#Current requests for edits to a protected page|request an' .. ' edit]] to this page, or [[Wikipedia:Requests for' .. ' page protection#Current requests for reduction in protection level' .. '|ask]] for it to be unprotected.' } }, move = { default = { subject = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' The page may still be edited but cannot be moved' .. ' until unprotected. Please discuss any suggested moves on the' .. ' ${TALKPAGE} or at [[Wikipedia:Requested moves]]. You can also' .. ' [[Wikipedia:Requests for page protection|request]] that the page be' .. ' unprotected.', default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' The page may still be edited but cannot be moved' .. ' until unprotected. Please discuss any suggested moves at' .. ' [[Wikipedia:Requested moves]]. You can also' .. ' [[Wikipedia:Requests for page protection|request]] that the page be' .. ' unprotected.' } }, autoreview = { default = { default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' Edits to this ${PAGETYPE} by new and unregistered users' .. ' will not be visible to readers until they are accepted by' .. ' a reviewer. To avoid the need for your edits to be' .. ' reviewed, you may' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]], [[Special:Userlogin|log in]], or' .. ' [[Special:UserLogin/signup|create an account]].' }, }, upload = { default = { default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' The page may still be edited but new versions of the file' .. ' cannot be uploaded until it is unprotected. You can' .. ' request that a new version be uploaded by using a' .. ' [[Wikipedia:Edit requests|protected edit request]], or you' .. ' can [[Wikipedia:Requests for page protection|request]]' .. ' that the file be unprotected.' } } }, -------------------------------------------------------------------------------- -- Protection levels -------------------------------------------------------------------------------- -- This table provides the data for the ${PROTECTIONLEVEL} parameter, which -- produces a short label for different protection levels. It is sorted by -- protection action and protection level, and is checked in the following -- order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level -- 3. "edit" protection action, default protection level -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. protectionLevels = { edit = { default = 'protected', templateeditor = 'template-protected', extendedconfirmed = 'extended-protected', autoconfirmed = 'semi-protected', }, move = { default = 'move-protected' }, autoreview = { }, upload = { default = 'upload-protected' } }, -------------------------------------------------------------------------------- -- Images -------------------------------------------------------------------------------- -- This table lists different padlock images for each protection action and -- protection level. It is used if an image is not specified in any of the -- banner data tables, and if the page does not satisfy the conditions for using -- the ['image-filename-indef'] image. It is checked in the following order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level images = { edit = { default = 'Full-protection-shackle.svg', templateeditor = 'Template-protection-shackle.svg', extendedconfirmed = 'Extended-protection-shackle.svg', autoconfirmed = 'Semi-protection-shackle.svg' }, move = { default = 'Move-protection-shackle.svg', }, autoreview = { default = 'Pending-protection-shackle.svg' }, upload = { default = 'Upload-protection-shackle.svg' } }, -- Pages with a reason specified in this table will show the special "indef" -- padlock, defined in the 'image-filename-indef' message, if no expiry is set. indefImageReasons = { template = true }, -------------------------------------------------------------------------------- -- Image links -------------------------------------------------------------------------------- -- This table provides the data for the ${IMAGELINK} parameter, which gets -- the image link for small padlock icons based on the page's protection action -- and protection level. It is checked in the following order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level -- 3. "edit" protection action, default protection level -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. imageLinks = { edit = { default = 'Wikipedia:Protection policy#full', templateeditor = 'Wikipedia:Protection policy#template', extendedconfirmed = 'Wikipedia:Protection policy#extended', autoconfirmed = 'Wikipedia:Protection policy#semi' }, move = { default = 'Wikipedia:Protection policy#move' }, autoreview = { default = 'Wikipedia:Protection policy#pending' }, upload = { default = 'Wikipedia:Protection policy#upload' } }, -------------------------------------------------------------------------------- -- Padlock indicator names -------------------------------------------------------------------------------- -- This table provides the "name" attribute for the <indicator> extension tag -- with which small padlock icons are generated. All indicator tags on a page -- are displayed in alphabetical order based on this attribute, and with -- indicator tags with duplicate names, the last tag on the page wins. -- The attribute is chosen based on the protection action; table keys must be a -- protection action name or the string "default". padlockIndicatorNames = { autoreview = 'pp-autoreview', default = 'pp-default' }, -------------------------------------------------------------------------------- -- Protection categories -------------------------------------------------------------------------------- --[[ -- The protection categories are stored in the protectionCategories table. -- Keys to this table are made up of the following strings: -- -- 1. the expiry date -- 2. the namespace -- 3. the protection reason (e.g. "dispute" or "vandalism") -- 4. the protection level (e.g. "sysop" or "autoconfirmed") -- 5. the action (e.g. "edit" or "move") -- -- When the module looks up a category in the table, first it will will check to -- see a key exists that corresponds to all five parameters. For example, a -- user page semi-protected from vandalism for two weeks would have the key -- "temp-user-vandalism-autoconfirmed-edit". If no match is found, the module -- changes the first part of the key to "all" and checks the table again. It -- keeps checking increasingly generic key combinations until it finds the -- field, or until it reaches the key "all-all-all-all-all". -- -- The module uses a binary matrix to determine the order in which to search. -- This is best demonstrated by a table. In this table, the "0" values -- represent "all", and the "1" values represent the original data (e.g. -- "indef" or "file" or "vandalism"). -- -- expiry namespace reason level action -- order -- 1 1 1 1 1 1 -- 2 0 1 1 1 1 -- 3 1 0 1 1 1 -- 4 0 0 1 1 1 -- 5 1 1 0 1 1 -- 6 0 1 0 1 1 -- 7 1 0 0 1 1 -- 8 0 0 0 1 1 -- 9 1 1 1 0 1 -- 10 0 1 1 0 1 -- 11 1 0 1 0 1 -- 12 0 0 1 0 1 -- 13 1 1 0 0 1 -- 14 0 1 0 0 1 -- 15 1 0 0 0 1 -- 16 0 0 0 0 1 -- 17 1 1 1 1 0 -- 18 0 1 1 1 0 -- 19 1 0 1 1 0 -- 20 0 0 1 1 0 -- 21 1 1 0 1 0 -- 22 0 1 0 1 0 -- 23 1 0 0 1 0 -- 24 0 0 0 1 0 -- 25 1 1 1 0 0 -- 26 0 1 1 0 0 -- 27 1 0 1 0 0 -- 28 0 0 1 0 0 -- 29 1 1 0 0 0 -- 30 0 1 0 0 0 -- 31 1 0 0 0 0 -- 32 0 0 0 0 0 -- -- In this scheme the action has the highest priority, as it is the last -- to change, and the expiry has the least priority, as it changes the most. -- The priorities of the expiry, the protection level and the action are -- fixed, but the priorities of the reason and the namespace can be swapped -- through the use of the cfg.bannerDataNamespaceHasPriority table. --]] -- If the reason specified to the template is listed in this table, -- namespace data will take priority over reason data in the protectionCategories -- table. reasonsWithNamespacePriority = { vandalism = true, }, -- The string to use as a namespace key for the protectionCategories table for each -- namespace number. categoryNamespaceKeys = { [ 2] = 'user', [ 3] = 'user', [ 4] = 'project', [ 6] = 'file', [ 8] = 'mediawiki', [ 10] = 'template', [ 12] = 'project', [ 14] = 'category', [100] = 'portal', [828] = 'module', }, protectionCategories = { ['all|all|all|all|all'] = 'Wikipedia fully protected pages', ['all|all|office|all|all'] = 'Wikipedia Office-protected pages', ['all|all|reset|all|all'] = 'Wikipedia Office-protected pages', ['all|all|dmca|all|all'] = 'Wikipedia Office-protected pages', ['all|all|mainpage|all|all'] = 'Wikipedia fully protected main page files', ['all|all|all|extendedconfirmed|all'] = 'Wikipedia extended-confirmed-protected pages', ['all|all|ecp|extendedconfirmed|all'] = 'Wikipedia extended-confirmed-protected pages', ['all|template|all|all|edit'] = 'Wikipedia fully protected templates', ['all|all|all|autoconfirmed|edit'] = 'Wikipedia semi-protected pages', ['indef|all|all|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected pages', ['all|all|blp|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected biographies of living people', ['temp|all|blp|autoconfirmed|edit'] = 'Wikipedia temporarily semi-protected biographies of living people', ['all|all|dispute|autoconfirmed|edit'] = 'Wikipedia pages semi-protected due to dispute', ['all|all|sock|autoconfirmed|edit'] = 'Wikipedia pages semi-protected from banned users', ['all|all|vandalism|autoconfirmed|edit'] = 'Wikipedia pages semi-protected against vandalism', ['all|category|all|autoconfirmed|edit'] = 'Wikipedia semi-protected categories', ['all|file|all|autoconfirmed|edit'] = 'Wikipedia semi-protected files', ['all|portal|all|autoconfirmed|edit'] = 'Wikipedia semi-protected portals', ['all|project|all|autoconfirmed|edit'] = 'Wikipedia semi-protected project pages', ['all|talk|all|autoconfirmed|edit'] = 'Wikipedia semi-protected talk pages', ['all|template|all|autoconfirmed|edit'] = 'Wikipedia semi-protected templates', ['all|user|all|autoconfirmed|edit'] = 'Wikipedia semi-protected user and user talk pages', ['all|all|all|templateeditor|edit'] = 'Wikipedia template-protected pages other than templates and modules', ['all|template|all|templateeditor|edit'] = 'Wikipedia template-protected templates', ['all|template|all|templateeditor|move'] = 'Wikipedia template-protected templates', -- move-protected templates ['all|all|blp|sysop|edit'] = 'Wikipedia indefinitely protected biographies of living people', ['temp|all|blp|sysop|edit'] = 'Wikipedia temporarily protected biographies of living people', ['all|all|dispute|sysop|edit'] = 'Wikipedia pages protected due to dispute', ['all|all|sock|sysop|edit'] = 'Wikipedia pages protected from banned users', ['all|all|vandalism|sysop|edit'] = 'Wikipedia pages protected against vandalism', ['all|category|all|sysop|edit'] = 'Wikipedia fully protected categories', ['all|file|all|sysop|edit'] = 'Wikipedia fully protected files', ['all|project|all|sysop|edit'] = 'Wikipedia fully protected project pages', ['all|talk|all|sysop|edit'] = 'Wikipedia fully protected talk pages', ['all|template|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected templates', ['all|template|all|sysop|edit'] = 'Wikipedia fully protected templates', ['all|user|all|sysop|edit'] = 'Wikipedia fully protected user and user talk pages', ['all|module|all|all|edit'] = 'Wikipedia fully protected modules', ['all|module|all|templateeditor|edit'] = 'Wikipedia template-protected modules', ['all|module|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected modules', ['all|module|all|autoconfirmed|edit'] = 'Wikipedia semi-protected modules', ['all|all|all|sysop|move'] = 'Wikipedia move-protected pages', ['indef|all|all|sysop|move'] = 'Wikipedia indefinitely move-protected pages', ['all|all|dispute|sysop|move'] = 'Wikipedia pages move-protected due to dispute', ['all|all|vandalism|sysop|move'] = 'Wikipedia pages move-protected due to vandalism', ['all|portal|all|sysop|move'] = 'Wikipedia move-protected portals', ['all|project|all|sysop|move'] = 'Wikipedia move-protected project pages', ['all|talk|all|sysop|move'] = 'Wikipedia move-protected talk pages', ['all|template|all|sysop|move'] = 'Wikipedia move-protected templates', ['all|user|all|sysop|move'] = 'Wikipedia move-protected user and user talk pages', ['all|all|all|autoconfirmed|autoreview'] = 'Wikipedia pending changes protected pages', ['all|file|all|all|upload'] = 'Wikipedia upload-protected files', }, -------------------------------------------------------------------------------- -- Expiry category config -------------------------------------------------------------------------------- -- This table configures the expiry category behaviour for each protection -- action. -- * If set to true, setting that action will always categorise the page if -- an expiry parameter is not set. -- * If set to false, setting that action will never categorise the page. -- * If set to nil, the module will categorise the page if: -- 1) an expiry parameter is not set, and -- 2) a reason is provided, and -- 3) the specified reason is not blacklisted in the reasonsWithoutExpiryCheck -- table. expiryCheckActions = { edit = nil, move = false, autoreview = true, upload = false }, reasonsWithoutExpiryCheck = { blp = true, template = true, }, -------------------------------------------------------------------------------- -- Pagetypes -------------------------------------------------------------------------------- -- This table produces the page types available with the ${PAGETYPE} parameter. -- Keys are namespace numbers, or the string "default" for the default value. pagetypes = { [0] = 'article', [6] = 'file', [10] = 'template', [14] = 'category', [828] = 'module', default = 'page' }, -------------------------------------------------------------------------------- -- Strings marking indefinite protection -------------------------------------------------------------------------------- -- This table contains values passed to the expiry parameter that mean the page -- is protected indefinitely. indefStrings = { ['indef'] = true, ['indefinite'] = true, ['indefinitely'] = true, ['infinite'] = true, }, -------------------------------------------------------------------------------- -- Group hierarchy -------------------------------------------------------------------------------- -- This table maps each group to all groups that have a superset of the original -- group's page editing permissions. hierarchy = { sysop = {}, reviewer = {'sysop'}, filemover = {'sysop'}, templateeditor = {'sysop'}, extendedconfirmed = {'sysop'}, autoconfirmed = {'reviewer', 'filemover', 'templateeditor', 'extendedconfirmed'}, user = {'autoconfirmed'}, ['*'] = {'user'} }, -------------------------------------------------------------------------------- -- Wrapper templates and their default arguments -------------------------------------------------------------------------------- -- This table contains wrapper templates used with the module, and their -- default arguments. Templates specified in this table should contain the -- following invocation, and no other template content: -- -- {{#invoke:Protection banner|main}} -- -- If other content is desired, it can be added between -- <noinclude>...</noinclude> tags. -- -- When a user calls one of these wrapper templates, they will use the -- default arguments automatically. However, users can override any of the -- arguments. wrappers = { ['Template:Pp'] = {}, ['Template:Pp-extended'] = {'ecp'}, ['Template:Pp-blp'] = {'blp'}, -- we don't need Template:Pp-create ['Template:Pp-dispute'] = {'dispute'}, ['Template:Pp-main-page'] = {'mainpage'}, ['Template:Pp-move'] = {action = 'move', catonly = 'yes'}, ['Template:Pp-move-dispute'] = {'dispute', action = 'move', catonly = 'yes'}, -- we don't need Template:Pp-move-indef ['Template:Pp-move-vandalism'] = {'vandalism', action = 'move', catonly = 'yes'}, ['Template:Pp-office'] = {'office'}, ['Template:Pp-office-dmca'] = {'dmca'}, ['Template:Pp-pc'] = {action = 'autoreview', small = true}, ['Template:Pp-pc1'] = {action = 'autoreview', small = true}, ['Template:Pp-reset'] = {'reset'}, ['Template:Pp-semi-indef'] = {small = true}, ['Template:Pp-sock'] = {'sock'}, ['Template:Pp-template'] = {'template', small = true}, ['Template:Pp-upload'] = {action = 'upload'}, ['Template:Pp-usertalk'] = {'usertalk'}, ['Template:Pp-vandalism'] = {'vandalism'}, }, -------------------------------------------------------------------------------- -- -- MESSAGES -- -------------------------------------------------------------------------------- msg = { -------------------------------------------------------------------------------- -- Intro blurb and intro fragment -------------------------------------------------------------------------------- -- These messages specify what is produced by the ${INTROBLURB} and -- ${INTROFRAGMENT} parameters. If the protection is temporary they use the -- intro-blurb-expiry or intro-fragment-expiry, and if not they use -- intro-blurb-noexpiry or intro-fragment-noexpiry. -- It is possible to use banner parameters in these messages. ['intro-blurb-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY}.', ['intro-blurb-noexpiry'] = '${PROTECTIONBLURB}.', ['intro-fragment-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY},', ['intro-fragment-noexpiry'] = '${PROTECTIONBLURB}', -------------------------------------------------------------------------------- -- Tooltip blurb -------------------------------------------------------------------------------- -- These messages specify what is produced by the ${TOOLTIPBLURB} parameter. -- If the protection is temporary the tooltip-blurb-expiry message is used, and -- if not the tooltip-blurb-noexpiry message is used. -- It is possible to use banner parameters in these messages. ['tooltip-blurb-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY}.', ['tooltip-blurb-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}.', ['tooltip-fragment-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY},', ['tooltip-fragment-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}', -------------------------------------------------------------------------------- -- Special explanation blurb -------------------------------------------------------------------------------- -- An explanation blurb for pages that cannot be unprotected, e.g. for pages -- in the MediaWiki namespace. -- It is possible to use banner parameters in this message. ['explanation-blurb-nounprotect'] = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' Please discuss any changes on the ${TALKPAGE}; you' .. ' may ${EDITREQUEST} to ask an' .. ' [[Wikipedia:Administrators|administrator]] to make an edit if it' .. ' is [[Help:Minor edit#When to mark an edit as a minor edit' .. '|uncontroversial]] or supported by [[Wikipedia:Consensus' .. '|consensus]].', -------------------------------------------------------------------------------- -- Protection log display values -------------------------------------------------------------------------------- -- These messages determine the display values for the protection log link -- or the pending changes log link produced by the ${PROTECTIONLOG} parameter. -- It is possible to use banner parameters in these messages. ['protection-log-display'] = 'protection log', ['pc-log-display'] = 'pending changes log', -------------------------------------------------------------------------------- -- Current version display values -------------------------------------------------------------------------------- -- These messages determine the display values for the page history link -- or the move log link produced by the ${CURRENTVERSION} parameter. -- It is possible to use banner parameters in these messages. ['current-version-move-display'] = 'current title', ['current-version-edit-display'] = 'current version', -------------------------------------------------------------------------------- -- Talk page -------------------------------------------------------------------------------- -- This message determines the display value of the talk page link produced -- with the ${TALKPAGE} parameter. -- It is possible to use banner parameters in this message. ['talk-page-link-display'] = 'talk page', -------------------------------------------------------------------------------- -- Edit requests -------------------------------------------------------------------------------- -- This message determines the display value of the edit request link produced -- with the ${EDITREQUEST} parameter. -- It is possible to use banner parameters in this message. ['edit-request-display'] = 'submit an edit request', -------------------------------------------------------------------------------- -- Expiry date format -------------------------------------------------------------------------------- -- This is the format for the blurb expiry date. It should be valid input for -- the first parameter of the #time parser function. ['expiry-date-format'] = 'F j, Y "at" H:i e', -------------------------------------------------------------------------------- -- Tracking categories -------------------------------------------------------------------------------- -- These messages determine which tracking categories the module outputs. ['tracking-category-incorrect'] = 'Wikipedia pages with incorrect protection templates', ['tracking-category-template'] = 'Wikipedia template-protected pages other than templates and modules', -------------------------------------------------------------------------------- -- Images -------------------------------------------------------------------------------- -- These are images that are not defined by their protection action and protection level. ['image-filename-indef'] = 'Full-protection-shackle.svg', ['image-filename-default'] = 'Transparent.gif', -------------------------------------------------------------------------------- -- End messages -------------------------------------------------------------------------------- } -------------------------------------------------------------------------------- -- End configuration -------------------------------------------------------------------------------- } a20552ae38cb5253a4fa29aa126abc74215a589f Module:Convert/data 828 497 994 2023-05-10T03:20:18Z string2>Johnuniq 0 update from sandbox per [[Template talk:Convert#Module version 29]] Scribunto text/plain -- Conversion data used by [[Module:Convert]] which uses mw.loadData() for -- read-only access to this module so that it is loaded only once per page. -- See [[:en:Template:Convert/Transwiki guide]] if copying to another wiki. -- -- These data tables follow: -- all_units all properties for a unit, including default output -- default_exceptions exceptions for default output ('kg' and 'g' have different defaults) -- link_exceptions exceptions for links ('kg' and 'g' have different links) -- -- These tables are generated by a script which reads the wikitext of a page that -- documents the required properties of each unit; see [[:en:Module:Convert/doc]]. --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local all_units = { ["Gy"] = { _name1 = "gray", _symbol = "Gy", utype = "absorbed radiation dose", scale = 1, prefixes = 1, default = "rad", link = "Gray (unit)", }, ["rad"] = { _name1 = "rad", _symbol = "rad", utype = "absorbed radiation dose", scale = 0.01, prefixes = 1, default = "Gy", link = "Rad (unit)", }, ["cm/s2"] = { name1 = "centimetre per second squared", name1_us = "centimeter per second squared", name2 = "centimetres per second squared", name2_us = "centimeters per second squared", symbol = "cm/s<sup>2</sup>", utype = "acceleration", scale = 0.01, default = "ft/s2", link = "Gal (unit)", }, ["ft/s2"] = { name1 = "foot per second squared", name2 = "feet per second squared", symbol = "ft/s<sup>2</sup>", utype = "acceleration", scale = 0.3048, default = "m/s2", }, ["g0"] = { name1 = "standard gravity", name2 = "standard gravities", symbol = "''g''<sub>0</sub>", utype = "acceleration", scale = 9.80665, default = "m/s2", }, ["g-force"] = { name2 = "''g''", symbol = "''g''", utype = "acceleration", scale = 9.80665, default = "m/s2", link = "g-force", }, ["km/hs"] = { name1 = "kilometre per hour per second", name1_us = "kilometer per hour per second", name2 = "kilometres per hour per second", name2_us = "kilometers per hour per second", symbol = "km/(h⋅s)", utype = "acceleration", scale = 0.27777777777777779, default = "mph/s", link = "Acceleration", }, ["km/s2"] = { name1 = "kilometre per second squared", name1_us = "kilometer per second squared", name2 = "kilometres per second squared", name2_us = "kilometers per second squared", symbol = "km/s<sup>2</sup>", utype = "acceleration", scale = 1000, default = "mph/s", link = "Acceleration", }, ["m/s2"] = { name1 = "metre per second squared", name1_us = "meter per second squared", name2 = "metres per second squared", name2_us = "meters per second squared", symbol = "m/s<sup>2</sup>", utype = "acceleration", scale = 1, default = "ft/s2", }, ["mph/s"] = { name1 = "mile per hour per second", name2 = "miles per hour per second", symbol = "mph/s", utype = "acceleration", scale = 0.44704, default = "km/hs", link = "Acceleration", }, ["km/h/s"] = { target = "km/hs", }, ["standard gravity"] = { target = "g0", }, ["1000sqft"] = { name1 = "thousand square feet", name2 = "thousand square feet", symbol = "1000&nbsp;sq&nbsp;ft", utype = "area", scale = 92.90304, default = "m2", link = "Square foot", }, ["a"] = { _name1 = "are", _symbol = "a", utype = "area", scale = 100, prefixes = 1, default = "sqft", link = "Hectare#Are", }, ["acre"] = { symbol = "acre", usename = 1, utype = "area", scale = 4046.8564224, default = "ha", subdivs = { ["rood"] = { 4, default = "ha" }, ["sqperch"] = { 160, default = "ha" } }, }, ["acre-sing"] = { target = "acre", }, ["arpent"] = { symbol = "arpent", usename = 1, utype = "area", scale = 3418.89, default = "ha", }, ["cda"] = { name1 = "cuerda", symbol = "cda", utype = "area", scale = 3930.395625, default = "ha acre", }, ["daa"] = { name1 = "decare", symbol = "daa", utype = "area", scale = 1000, default = "km2 sqmi", }, ["dunam"] = { symbol = "dunam", usename = 1, utype = "area", scale = 1000, default = "km2 sqmi", }, ["dunum"] = { symbol = "dunum", usename = 1, utype = "area", scale = 1000, default = "km2 sqmi", link = "Dunam", }, ["ha"] = { name1 = "hectare", symbol = "ha", utype = "area", scale = 10000, default = "acre", }, ["hectare"] = { name1 = "hectare", symbol = "ha", usename = 1, utype = "area", scale = 10000, default = "acre", }, ["Irish acre"] = { name1 = "Irish acre", symbol = "Irish&nbsp;acres", utype = "area", scale = 6555.2385024, default = "ha", link = "Acre (Irish)", }, ["m2"] = { _name1 = "square metre", _name1_us= "square meter", _symbol = "m<sup>2</sup>", prefix_position= 8, utype = "area", scale = 1, prefixes = 2, default = "sqft", link = "Square metre", }, ["pondemaat"] = { name1 = "pondemaat", name2 = "pondemaat", symbol = "pond", utype = "area", scale = 3674.363358816, default = "m2", link = ":nl:pondemaat", }, ["pyeong"] = { name2 = "pyeong", symbol = "pyeong", usename = 1, utype = "area", scale = 3.3057851239669422, default = "m2", }, ["rai"] = { name2 = "rai", symbol = "rai", utype = "area", scale = 1600, default = "m2", link = "Rai (unit)", }, ["rood"] = { symbol = "rood", usename = 1, utype = "area", scale = 1011.7141056, default = "sqft m2", subdivs = { ["sqperch"] = { 40, default = "m2" } }, link = "Rood (unit)", }, ["sqfoot"] = { name1 = "square foot", name2 = "square foot", symbol = "sq&nbsp;ft", utype = "area", scale = 0.09290304, default = "m2", }, ["sqft"] = { name1 = "square foot", name2 = "square feet", symbol = "sq&nbsp;ft", utype = "area", scale = 0.09290304, default = "m2", }, ["sqin"] = { name1 = "square inch", name2 = "square inches", symbol = "sq&nbsp;in", utype = "area", scale = 0.00064516, default = "cm2", }, ["sqmi"] = { name1 = "square mile", symbol = "sq&nbsp;mi", utype = "area", scale = 2589988.110336, default = "km2", }, ["sqnmi"] = { name1 = "square nautical mile", symbol = "sq&nbsp;nmi", utype = "area", scale = 3429904, default = "km2 sqmi", link = "Nautical mile", }, ["sqperch"] = { name2 = "perches", symbol = "perch", usename = 1, utype = "area", scale = 25.29285264, default = "m2", link = "Rod (unit)#Area and volume", }, ["sqverst"] = { symbol = "square verst", usename = 1, utype = "area", scale = 1138062.24, default = "km2 sqmi", link = "Verst", }, ["sqyd"] = { name1 = "square yard", symbol = "sq&nbsp;yd", utype = "area", scale = 0.83612736, default = "m2", }, ["tsubo"] = { name2 = "tsubo", symbol = "tsubo", usename = 1, utype = "area", scale = 3.3057851239669422, default = "m2", link = "Japanese units of measurement#Area", }, ["acres"] = { target = "acre", }, ["are"] = { target = "a", }, ["decare"] = { target = "daa", }, ["foot2"] = { target = "sqfoot", }, ["ft2"] = { target = "sqft", }, ["in2"] = { target = "sqin", symbol = "in<sup>2</sup>", }, ["km²"] = { target = "km2", }, ["mi2"] = { target = "sqmi", }, ["million acre"] = { target = "e6acre", }, ["million acres"] = { target = "e6acre", }, ["million hectares"] = { target = "e6ha", }, ["m²"] = { target = "m2", }, ["nmi2"] = { target = "sqnmi", }, ["pond"] = { target = "pondemaat", }, ["sq arp"] = { target = "arpent", }, ["sqkm"] = { target = "km2", }, ["sqm"] = { target = "m2", }, ["square verst"] = { target = "sqverst", }, ["verst2"] = { target = "sqverst", }, ["yd2"] = { target = "sqyd", }, ["m2/ha"] = { name1 = "square metre per hectare", name1_us = "square meter per hectare", name2 = "square metres per hectare", name2_us = "square meters per hectare", symbol = "m<sup>2</sup>/ha", utype = "area per unit area", scale = 0.0001, default = "sqft/acre", link = "Basal area", }, ["sqft/acre"] = { name1 = "square foot per acre", name2 = "square feet per acre", symbol = "sq&nbsp;ft/acre", utype = "area per unit area", scale = 2.295684113865932e-5, default = "m2/ha", link = "Basal area", }, ["cent"] = { name1 = "cent", symbol = "¢", utype = "cent", scale = 1, default = "cent", link = "Cent (currency)", }, ["¢"] = { target = "cent", }, ["A.h"] = { name1 = "ampere hour", symbol = "A⋅h", utype = "charge", scale = 3600, default = "coulomb", }, ["coulomb"] = { _name1 = "coulomb", _symbol = "C", utype = "charge", scale = 1, prefixes = 1, default = "e", link = "Coulomb", }, ["e"] = { name1 = "elementary charge", symbol = "''e''", utype = "charge", scale = 1.602176487e-19, default = "coulomb", }, ["g-mol"] = { name1 = "gram-mole", symbol = "g&#8209;mol", utype = "chemical amount", scale = 1, default = "lbmol", link = "Mole (unit)", }, ["gmol"] = { name1 = "gram-mole", symbol = "gmol", utype = "chemical amount", scale = 1, default = "lbmol", link = "Mole (unit)", }, ["kmol"] = { name1 = "kilomole", symbol = "kmol", utype = "chemical amount", scale = 1000, default = "lbmol", link = "Mole (unit)", }, ["lb-mol"] = { name1 = "pound-mole", symbol = "lb&#8209;mol", utype = "chemical amount", scale = 453.59237, default = "mol", }, ["lbmol"] = { name1 = "pound-mole", symbol = "lbmol", utype = "chemical amount", scale = 453.59237, default = "mol", }, ["mol"] = { name1 = "mole", symbol = "mol", utype = "chemical amount", scale = 1, default = "lbmol", link = "Mole (unit)", }, ["kgCO2/L"] = { name1 = "kilogram per litre", name1_us = "kilogram per liter", name2 = "kilograms per litre", name2_us = "kilograms per liter", symbol = "kg(CO<sub>2</sub>)/L", utype = "co2 per unit volume", scale = 1000, default = "lbCO2/USgal", link = "Exhaust gas", }, ["lbCO2/USgal"] = { name1 = "pound per US gallon", name2 = "pounds per US gallon", symbol = "lbCO2/US&nbsp;gal", utype = "co2 per unit volume", scale = 119.82642731689663, default = "kgCO2/L", link = "Exhaust gas", }, ["oz/lb"] = { per = { "oz", "lb" }, utype = "concentration", default = "mg/kg", }, ["mg/kg"] = { per = { "mg", "kg" }, utype = "concentration", default = "oz/lb", }, ["g/dm3"] = { name1 = "gram per cubic decimetre", name1_us = "gram per cubic decimeter", name2 = "grams per cubic decimetre", name2_us = "grams per cubic decimeter", symbol = "g/dm<sup>3</sup>", utype = "density", scale = 1, default = "kg/m3", link = "Density", }, ["g/L"] = { name1 = "gram per litre", name1_us = "gram per liter", name2 = "grams per litre", name2_us = "grams per liter", symbol = "g/L", utype = "density", scale = 1, default = "lb/cuin", link = "Density", }, ["g/mL"] = { name1 = "gram per millilitre", name1_us = "gram per milliliter", name2 = "grams per millilitre", name2_us = "grams per milliliter", symbol = "g/mL", utype = "density", scale = 1000, default = "lb/cuin", link = "Density", }, ["g/ml"] = { name1 = "gram per millilitre", name1_us = "gram per milliliter", name2 = "grams per millilitre", name2_us = "grams per milliliter", symbol = "g/ml", utype = "density", scale = 1000, default = "lb/cuin", link = "Density", }, ["kg/dm3"] = { name1 = "kilogram per cubic decimetre", name1_us = "kilogram per cubic decimeter", name2 = "kilograms per cubic decimetre", name2_us = "kilograms per cubic decimeter", symbol = "kg/dm<sup>3</sup>", utype = "density", scale = 1000, default = "lb/cuft", link = "Density", }, ["kg/L"] = { name1 = "kilogram per litre", name1_us = "kilogram per liter", name2 = "kilograms per litre", name2_us = "kilograms per liter", symbol = "kg/L", utype = "density", scale = 1000, default = "lb/USgal", link = "Density", }, ["kg/l"] = { name1 = "kilogram per litre", name1_us = "kilogram per liter", name2 = "kilograms per litre", name2_us = "kilograms per liter", symbol = "kg/l", utype = "density", scale = 1000, default = "lb/USgal", link = "Density", }, ["kg/m3"] = { name1 = "kilogram per cubic metre", name1_us = "kilogram per cubic meter", name2 = "kilograms per cubic metre", name2_us = "kilograms per cubic meter", symbol = "kg/m<sup>3</sup>", utype = "density", scale = 1, default = "lb/cuyd", link = "Density", }, ["lb/cuft"] = { name1 = "pound per cubic foot", name2 = "pounds per cubic foot", symbol = "lb/cu&nbsp;ft", utype = "density", scale = 16.018463373960142, default = "g/cm3", link = "Density", }, ["lb/cuin"] = { name1 = "pound per cubic inch", name2 = "pounds per cubic inch", symbol = "lb/cu&nbsp;in", utype = "density", scale = 27679.904710203122, default = "g/cm3", link = "Density", }, ["lb/cuyd"] = { name1 = "pound per cubic yard", name2 = "pounds per cubic yard", symbol = "lb/cu&nbsp;yd", utype = "density", scale = 0.5932764212577829, default = "kg/m3", link = "Density", }, ["lb/impgal"] = { name1 = "pound per imperial gallon", name2 = "pounds per imperial gallon", symbol = "lb/imp&nbsp;gal", utype = "density", scale = 99.776372663101697, default = "kg/L", link = "Density", }, ["lb/in3"] = { name1 = "pound per cubic inch", name2 = "pounds per cubic inch", symbol = "lb/cu&thinsp;in", utype = "density", scale = 27679.904710203122, default = "g/cm3", link = "Density", }, ["lb/U.S.gal"] = { name1 = "pound per U.S. gallon", name2 = "pounds per U.S. gallon", symbol = "lb/U.S.&nbsp;gal", utype = "density", scale = 119.82642731689663, default = "kg/L", link = "Density", }, ["lb/USbu"] = { name1 = "pound per US bushel", name2 = "pounds per US bushel", symbol = "lb/US&nbsp;bu", utype = "density", scale = 12.871859780974471, default = "kg/m3", link = "Bushel", }, ["lb/USgal"] = { name1 = "pound per US gallon", name2 = "pounds per US gallon", symbol = "lb/US&nbsp;gal", utype = "density", scale = 119.82642731689663, default = "kg/L", link = "Density", }, ["lbm/cuin"] = { name1 = "pound mass per cubic inch", name2 = "pounds mass per cubic inch", symbol = "lbm/cu&thinsp;in", utype = "density", scale = 27679.904710203122, default = "g/cm3", link = "Density", }, ["mg/L"] = { name1 = "milligram per litre", name1_us = "milligram per liter", name2 = "milligrams per litre", name2_us = "milligrams per liter", symbol = "mg/L", utype = "density", scale = 0.001, default = "lb/cuin", link = "Density", }, ["oz/cuin"] = { name1 = "ounce per cubic inch", name2 = "ounces per cubic inch", symbol = "oz/cu&nbsp;in", utype = "density", scale = 1729.9940443876951, default = "g/cm3", link = "Density", }, ["g/cm3"] = { per = { "g", "cm3" }, utype = "density", default = "lb/cuin", }, ["g/m3"] = { per = { "g", "m3" }, utype = "density", default = "lb/cuyd", link = "Density", }, ["Mg/m3"] = { per = { "Mg", "m3" }, utype = "density", default = "lb/cuft", }, ["mg/l"] = { per = { "mg", "ll" }, utype = "density", default = "oz/cuin", }, ["μg/dL"] = { per = { "μg", "dL" }, utype = "density", default = "lb/cuin", }, ["μg/l"] = { per = { "μg", "ll" }, utype = "density", default = "oz/cuin", }, ["lb/ft3"] = { target = "lb/cuft", }, ["lb/yd3"] = { target = "lb/cuyd", }, ["lbm/in3"] = { target = "lbm/cuin", }, ["mcg/dL"] = { target = "μg/dL", }, ["oz/in3"] = { target = "oz/cuin", }, ["ug/dL"] = { target = "μg/dL", }, ["ug/l"] = { target = "μg/l", }, ["B.O.T.U."] = { name1 = "Board of Trade Unit", symbol = "B.O.T.U.", utype = "energy", scale = 3600000, default = "MJ", link = "Kilowatt-hour", }, ["bboe"] = { name1 = "barrel of oil equivalent", name2 = "barrels of oil equivalent", symbol = "bboe", utype = "energy", scale = 6117863200, default = "GJ", }, ["BOE"] = { name1 = "barrel of oil equivalent", name2 = "barrels of oil equivalent", symbol = "BOE", utype = "energy", scale = 6117863200, default = "GJ", }, ["BTU"] = { name1 = "British thermal unit", symbol = "BTU", utype = "energy", scale = 1055.05585262, default = "kJ", }, ["Btu"] = { name1 = "British thermal unit", symbol = "Btu", utype = "energy", scale = 1055.05585262, default = "kJ", }, ["BTU-39F"] = { name1 = "British thermal unit (39°F)", name2 = "British thermal units (39°F)", symbol = "BTU<sub>39°F</sub>", utype = "energy", scale = 1059.67, default = "kJ", link = "British thermal unit", }, ["Btu-39F"] = { name1 = "British thermal unit (39°F)", name2 = "British thermal units (39°F)", symbol = "Btu<sub>39°F</sub>", utype = "energy", scale = 1059.67, default = "kJ", link = "British thermal unit", }, ["BTU-59F"] = { name1 = "British thermal unit (59°F)", name2 = "British thermal units (59°F)", symbol = "BTU<sub>59°F</sub>", utype = "energy", scale = 1054.804, default = "kJ", link = "British thermal unit", }, ["Btu-59F"] = { name1 = "British thermal unit (59°F)", name2 = "British thermal units (59°F)", symbol = "Btu<sub>59°F</sub>", utype = "energy", scale = 1054.804, default = "kJ", link = "British thermal unit", }, ["BTU-60F"] = { name1 = "British thermal unit (60°F)", name2 = "British thermal units (60°F)", symbol = "BTU<sub>60°F</sub>", utype = "energy", scale = 1054.68, default = "kJ", link = "British thermal unit", }, ["Btu-60F"] = { name1 = "British thermal unit (60°F)", name2 = "British thermal units (60°F)", symbol = "Btu<sub>60°F</sub>", utype = "energy", scale = 1054.68, default = "kJ", link = "British thermal unit", }, ["BTU-63F"] = { name1 = "British thermal unit (63°F)", name2 = "British thermal units (63°F)", symbol = "BTU<sub>63°F</sub>", utype = "energy", scale = 1054.6, default = "kJ", link = "British thermal unit", }, ["Btu-63F"] = { name1 = "British thermal unit (63°F)", name2 = "British thermal units (63°F)", symbol = "Btu<sub>63°F</sub>", utype = "energy", scale = 1054.6, default = "kJ", link = "British thermal unit", }, ["BTU-ISO"] = { name1 = "British thermal unit (ISO)", name2 = "British thermal units (ISO)", symbol = "BTU<sub>ISO</sub>", utype = "energy", scale = 1055.056, default = "kJ", link = "British thermal unit", }, ["Btu-ISO"] = { target = "BTU-ISO", }, ["BTU-IT"] = { name1 = "British thermal unit (IT)", name2 = "British thermal units (IT)", symbol = "BTU<sub>IT</sub>", utype = "energy", scale = 1055.05585262, default = "kJ", link = "British thermal unit", }, ["Btu-IT"] = { name1 = "British thermal unit (IT)", name2 = "British thermal units (IT)", symbol = "Btu<sub>IT</sub>", utype = "energy", scale = 1055.05585262, default = "kJ", link = "British thermal unit", }, ["BTU-mean"] = { name1 = "British thermal unit (mean)", name2 = "British thermal units (mean)", symbol = "BTU<sub>mean</sub>", utype = "energy", scale = 1055.87, default = "kJ", link = "British thermal unit", }, ["Btu-mean"] = { name1 = "British thermal unit (mean)", name2 = "British thermal units (mean)", symbol = "Btu<sub>mean</sub>", utype = "energy", scale = 1055.87, default = "kJ", link = "British thermal unit", }, ["BTU-th"] = { name1 = "British thermal unit (thermochemical)", name2 = "British thermal units (thermochemical)", symbol = "BTU<sub>th</sub>", utype = "energy", scale = 1054.35026444, default = "kJ", link = "British thermal unit", }, ["Btu-th"] = { name1 = "British thermal unit (thermochemical)", name2 = "British thermal units (thermochemical)", symbol = "Btu<sub>th</sub>", utype = "energy", scale = 1054.35026444, default = "kJ", link = "British thermal unit", }, ["Cal"] = { name1 = "calorie", symbol = "Cal", utype = "energy", scale = 4184, default = "kJ", }, ["cal"] = { name1 = "calorie", symbol = "cal", utype = "energy", scale = 4.184, default = "J", }, ["Cal-15"] = { name1 = "Calorie (15°C)", name2 = "Calories (15°C)", symbol = "Cal<sub>15</sub>", utype = "energy", scale = 4185.8, default = "kJ", link = "Calorie", }, ["cal-15"] = { name1 = "calorie (15°C)", name2 = "calories (15°C)", symbol = "cal<sub>15</sub>", utype = "energy", scale = 4.1858, default = "J", link = "Calorie", }, ["Cal-IT"] = { name1 = "Calorie (International Steam Table)", name2 = "Calories (International Steam Table)", symbol = "Cal<sub>IT</sub>", utype = "energy", scale = 4186.8, default = "kJ", link = "Calorie", }, ["cal-IT"] = { name1 = "calorie (International Steam Table)", name2 = "calories (International Steam Table)", symbol = "cal<sub>IT</sub>", utype = "energy", scale = 4.1868, default = "J", link = "Calorie", }, ["Cal-th"] = { name1 = "Calorie (thermochemical)", name2 = "Calories (thermochemical)", symbol = "Cal<sub>th</sub>", utype = "energy", scale = 4184, default = "kJ", link = "Calorie", }, ["cal-th"] = { name1 = "calorie (thermochemical)", name2 = "calories (thermochemical)", symbol = "cal<sub>th</sub>", utype = "energy", scale = 4.184, default = "J", link = "Calorie", }, ["CHU-IT"] = { name1 = "Celsius heat unit (International Table)", name2 = "Celsius heat units (International Table)", symbol = "CHU<sub>IT</sub>", utype = "energy", scale = 1899.100534716, default = "kJ", link = "Conversion of units#Energy", }, ["cufootnaturalgas"] = { name1 = "cubic foot of natural gas", name2 = "cubic foot of natural gas", symbol = "cuftnaturalgas", usename = 1, utype = "energy", scale = 1055055.85262, default = "MJ", link = "Conversion of units#Energy", }, ["cuftnaturalgas"] = { name1 = "cubic foot of natural gas", name2 = "cubic feet of natural gas", symbol = "cuftnaturalgas", usename = 1, utype = "energy", scale = 1055055.85262, default = "MJ", link = "Conversion of units#Energy", }, ["Eh"] = { name1 = "Hartree", symbol = "''E''<sub>h</sub>", utype = "energy", scale = 4.35974417e-18, default = "eV", }, ["erg"] = { symbol = "erg", utype = "energy", scale = 0.0000001, default = "μJ", }, ["eV"] = { name1 = "electronvolt", symbol = "eV", utype = "energy", scale = 1.602176487e-19, default = "aJ", }, ["feV"] = { name1 = "femtoelectronvolt", symbol = "feV", utype = "energy", scale = 1.602176487e-34, default = "yJ", link = "Electronvolt", }, ["foe"] = { symbol = "foe", utype = "energy", scale = 1e44, default = "YJ", link = "Foe (unit)", }, ["ftlb"] = { name1 = "foot-pound", symbol = "ft⋅lb", utype = "energy", alttype = "torque", scale = 1.3558179483314004, default = "J", link = "Foot-pound (energy)", }, ["ftlb-f"] = { name1 = "foot-pound force", name2 = "foot-pounds force", symbol = "ft⋅lb<sub>f</sub>", utype = "energy", alttype = "torque", scale = 1.3558179483314004, default = "J", link = "Foot-pound (energy)", }, ["ftlbf"] = { name1 = "foot-pound force", name2 = "foot-pounds force", symbol = "ft⋅lbf", utype = "energy", alttype = "torque", scale = 1.3558179483314004, default = "J", link = "Foot-pound (energy)", }, ["ftpdl"] = { name1 = "foot-poundal", symbol = "ft⋅pdl", utype = "energy", scale = 0.0421401100938048, default = "J", }, ["GeV"] = { name1 = "gigaelectronvolt", symbol = "GeV", utype = "energy", scale = 1.602176487e-10, default = "nJ", link = "Electronvolt", }, ["gTNT"] = { name2 = "grams of TNT", symbol = "gram of TNT", usename = 1, utype = "energy", scale = 4184, default = "kJ", link = "TNT equivalent", }, ["Gtoe"] = { name1 = "gigatonne of oil equivalent", name2 = "gigatonnes of oil equivalent", symbol = "Gtoe", utype = "energy", scale = 4.1868e19, default = "EJ", link = "Tonne of oil equivalent", }, ["GtonTNT"] = { name2 = "gigatons of TNT", symbol = "gigaton of TNT", usename = 1, utype = "energy", scale = 4.184e18, default = "EJ", link = "TNT equivalent", }, ["GtTNT"] = { name2 = "gigatonnes of TNT", symbol = "gigatonne of TNT", usename = 1, utype = "energy", scale = 4.184e18, default = "EJ", link = "TNT equivalent", }, ["GW.h"] = { name1 = "gigawatt-hour", symbol = "GW⋅h", utype = "energy", scale = 3.6e12, default = "TJ", link = "Kilowatt-hour", }, ["GWh"] = { name1 = "gigawatt-hour", symbol = "GWh", utype = "energy", scale = 3.6e12, default = "TJ", link = "Kilowatt-hour", }, ["hph"] = { name1 = "horsepower-hour", symbol = "hp⋅h", utype = "energy", scale = 2684519.537696172792, default = "kWh", link = "Horsepower", }, ["inlb"] = { name1 = "inch-pound", symbol = "in⋅lb", utype = "energy", alttype = "torque", scale = 0.1129848290276167, default = "mJ", link = "Foot-pound (energy)", }, ["inlb-f"] = { name1 = "inch-pound force", name2 = "inch-pounds force", symbol = "in⋅lb<sub>f</sub>", utype = "energy", alttype = "torque", scale = 0.1129848290276167, default = "mJ", link = "Foot-pound (energy)", }, ["inlbf"] = { name1 = "inch-pound force", name2 = "inch-pounds force", symbol = "in⋅lbf", utype = "energy", alttype = "torque", scale = 0.1129848290276167, default = "mJ", link = "Foot-pound (energy)", }, ["inoz-f"] = { name1 = "inch-ounce force", name2 = "inch-ounces force", symbol = "in⋅oz<sub>f</sub>", utype = "energy", alttype = "torque", scale = 0.00706155181422604375, default = "mJ", link = "Foot-pound (energy)", }, ["inozf"] = { name1 = "inch-ounce force", name2 = "inch-ounces force", symbol = "in⋅ozf", utype = "energy", alttype = "torque", scale = 0.00706155181422604375, default = "mJ", link = "Foot-pound (energy)", }, ["J"] = { _name1 = "joule", _symbol = "J", utype = "energy", scale = 1, prefixes = 1, default = "cal", link = "Joule", }, ["kBOE"] = { name1 = "kilo barrel of oil equivalent", name2 = "kilo barrels of oil equivalent", symbol = "kBOE", utype = "energy", scale = 6.1178632e12, default = "TJ", link = "Barrel of oil equivalent", }, ["kcal"] = { name1 = "kilocalorie", symbol = "kcal", utype = "energy", scale = 4184, default = "kJ", link = "Calorie", }, ["kcal-15"] = { name1 = "kilocalorie (15°C)", name2 = "kilocalories (15°C)", symbol = "kcal<sub>15</sub>", utype = "energy", scale = 4185.8, default = "kJ", link = "Calorie", }, ["kcal-IT"] = { name1 = "kilocalorie (International Steam Table)", name2 = "kilocalories (International Steam Table)", symbol = "kcal<sub>IT</sub>", utype = "energy", scale = 4186.8, default = "kJ", link = "Calorie", }, ["kcal-th"] = { name1 = "kilocalorie (thermochemical)", name2 = "kilocalories (thermochemical)", symbol = "kcal<sub>th</sub>", utype = "energy", scale = 4184, default = "kJ", link = "Calorie", }, ["kerg"] = { name1 = "kiloerg", symbol = "kerg", utype = "energy", scale = 0.0001, default = "mJ", link = "Erg", }, ["keV"] = { name1 = "kiloelectronvolt", symbol = "keV", utype = "energy", scale = 1.602176487e-16, default = "fJ", link = "Electronvolt", }, ["kgTNT"] = { name2 = "kilograms of TNT", symbol = "kilogram of TNT", usename = 1, utype = "energy", scale = 4184000, default = "MJ", link = "TNT equivalent", }, ["kt(TNT)"] = { name1 = "kilotonne", name1_us = "kiloton", symbol = "kt", utype = "energy", scale = 4.184e12, default = "TJ", link = "TNT equivalent", }, ["ktoe"] = { name1 = "kilotonne of oil equivalent", name2 = "kilotonnes of oil equivalent", symbol = "ktoe", utype = "energy", scale = 4.1868e13, default = "TJ", link = "Tonne of oil equivalent", }, ["ktonTNT"] = { name1 = "kiloton of TNT", name2 = "kilotons of TNT", symbol = "kt", utype = "energy", scale = 4.184e12, default = "TJ", link = "TNT equivalent", }, ["ktTNT"] = { name2 = "kilotonnes of TNT", symbol = "kilotonne of TNT", usename = 1, utype = "energy", scale = 4.184e12, default = "TJ", link = "TNT equivalent", }, ["kW.h"] = { name1 = "kilowatt-hour", symbol = "kW⋅h", utype = "energy", scale = 3600000, default = "MJ", }, ["kWh"] = { name1 = "kilowatt-hour", symbol = "kWh", utype = "energy", scale = 3600000, default = "MJ", }, ["Mcal"] = { name1 = "megacalorie", symbol = "Mcal", utype = "energy", scale = 4184000, default = "MJ", link = "Calorie", }, ["mcal"] = { name1 = "millicalorie", symbol = "mcal", utype = "energy", scale = 0.004184, default = "mJ", link = "Calorie", }, ["Mcal-15"] = { name1 = "megacalorie (15°C)", name2 = "megacalories (15°C)", symbol = "Mcal<sub>15</sub>", utype = "energy", scale = 4185800, default = "MJ", link = "Calorie", }, ["mcal-15"] = { name1 = "millicalorie (15°C)", name2 = "millicalories (15°C)", symbol = "mcal<sub>15</sub>", utype = "energy", scale = 0.0041858, default = "mJ", link = "Calorie", }, ["Mcal-IT"] = { name1 = "megacalorie (International Steam Table)", name2 = "megacalories (International Steam Table)", symbol = "Mcal<sub>IT</sub>", utype = "energy", scale = 4186800, default = "MJ", link = "Calorie", }, ["mcal-IT"] = { name1 = "millicalorie (International Steam Table)", name2 = "millicalories (International Steam Table)", symbol = "mcal<sub>IT</sub>", utype = "energy", scale = 0.0041868, default = "mJ", link = "Calorie", }, ["Mcal-th"] = { name1 = "megacalorie (thermochemical)", name2 = "megacalories (thermochemical)", symbol = "Mcal<sub>th</sub>", utype = "energy", scale = 4184000, default = "MJ", link = "Calorie", }, ["mcal-th"] = { name1 = "millicalorie (thermochemical)", name2 = "millicalories (thermochemical)", symbol = "mcal<sub>th</sub>", utype = "energy", scale = 0.004184, default = "mJ", link = "Calorie", }, ["Merg"] = { name1 = "megaerg", symbol = "Merg", utype = "energy", scale = 0.1, default = "J", link = "Erg", }, ["merg"] = { name1 = "millierg", symbol = "merg", utype = "energy", scale = 0.0000000001, default = "μJ", link = "Erg", }, ["MeV"] = { name1 = "megaelectronvolt", symbol = "MeV", utype = "energy", scale = 1.602176487e-13, default = "pJ", link = "Electronvolt", }, ["meV"] = { name1 = "millielectronvolt", symbol = "meV", utype = "energy", scale = 1.602176487e-22, default = "zJ", link = "Electronvolt", }, ["MMBtu"] = { name1 = "million British thermal units", name2 = "million British thermal units", symbol = "MMBtu", utype = "energy", scale = 1055055852.62, default = "GJ", link = "British thermal unit", }, ["Mt(TNT)"] = { name1 = "megatonne", name1_us = "megaton", symbol = "Mt", utype = "energy", scale = 4.184e15, default = "PJ", link = "TNT equivalent", }, ["Mtoe"] = { name1 = "megatonne of oil equivalent", name2 = "megatonnes of oil equivalent", symbol = "Mtoe", utype = "energy", scale = 4.1868e16, default = "PJ", link = "Tonne of oil equivalent", }, ["MtonTNT"] = { name1 = "megaton of TNT", name2 = "megatons of TNT", symbol = "Mt", utype = "energy", scale = 4.184e15, default = "PJ", link = "TNT equivalent", }, ["mtonTNT"] = { name2 = "millitons of TNT", symbol = "milliton of TNT", usename = 1, utype = "energy", scale = 4184000, default = "MJ", link = "TNT equivalent", }, ["MtTNT"] = { name2 = "megatonnes of TNT", symbol = "megatonne of TNT", usename = 1, utype = "energy", scale = 4.184e15, default = "PJ", link = "TNT equivalent", }, ["mtTNT"] = { name2 = "millitonnes of TNT", symbol = "millitonne of TNT", usename = 1, utype = "energy", scale = 4184000, default = "MJ", link = "TNT equivalent", }, ["MW.h"] = { name1 = "megawatt-hour", symbol = "MW⋅h", utype = "energy", scale = 3600000000, default = "GJ", link = "Kilowatt-hour", }, ["mW.h"] = { name1 = "milliwatt-hour", symbol = "mW⋅h", utype = "energy", scale = 3.6, default = "J", link = "Kilowatt-hour", }, ["MWh"] = { name1 = "megawatt-hour", symbol = "MWh", utype = "energy", scale = 3600000000, default = "GJ", link = "Kilowatt-hour", }, ["mWh"] = { name1 = "milliwatt-hour", symbol = "mWh", utype = "energy", scale = 3.6, default = "J", link = "Kilowatt-hour", }, ["neV"] = { name1 = "nanoelectronvolt", symbol = "neV", utype = "energy", scale = 1.602176487e-28, default = "yJ", link = "Electronvolt", }, ["PeV"] = { name1 = "petaelectronvolt", symbol = "PeV", utype = "energy", scale = 0.0001602176487, default = "mJ", link = "Electronvolt", }, ["peV"] = { name1 = "picoelectronvolt", symbol = "peV", utype = "energy", scale = 1.602176487e-31, default = "yJ", link = "Electronvolt", }, ["PSh"] = { name1 = "Pferdestärkenstunde", symbol = "PSh", utype = "energy", scale = 2647795.5, default = "kWh", }, ["quad"] = { name1 = "quadrillion British thermal units", name2 = "quadrillion British thermal units", symbol = "quad", utype = "energy", scale = 1.054804e18, default = "EJ", link = "Quad (unit)", }, ["Ry"] = { name1 = "rydberg", symbol = "Ry", utype = "energy", scale = 2.1798741e-18, default = "eV", link = "Rydberg constant", }, ["scf"] = { name1 = "standard cubic foot", name2 = "standard cubic feet", symbol = "scf", utype = "energy", scale = 2869.2044809344, default = "kJ", }, ["scfoot"] = { name1 = "standard cubic foot", name2 = "standard cubic foot", symbol = "scf", utype = "energy", scale = 2869.2044809344, default = "kJ", }, ["t(TNT)"] = { name1 = "tonne", name1_us = "ton", symbol = "t", utype = "energy", scale = 4184000000, default = "GJ", link = "TNT equivalent", }, ["TeV"] = { name1 = "teraelectronvolt", symbol = "TeV", utype = "energy", scale = 1.602176487e-7, default = "μJ", link = "Electronvolt", }, ["th"] = { name1 = "thermie", symbol = "th", utype = "energy", scale = 4186800, default = "MJ", link = "Conversion of units#Energy", }, ["thm-EC"] = { name1 = "therm (EC)", name2 = "therms (EC)", symbol = "thm (EC)", utype = "energy", scale = 105506000, default = "MJ", link = "Therm", }, ["thm-UK"] = { name1 = "therm (UK)", name2 = "therms (UK)", symbol = "thm (UK)", utype = "energy", scale = 105505585.257348, default = "MJ", link = "Therm", }, ["thm-US"] = { name1 = "therm (US)", name1_us = "therm (U.S.)", name2 = "therms (US)", name2_us = "therms (U.S.)", symbol = "thm (US)", sym_us = "thm (U.S.)", utype = "energy", scale = 105480400, default = "MJ", link = "Therm", }, ["toe"] = { name1 = "tonne of oil equivalent", name2 = "tonnes of oil equivalent", symbol = "toe", utype = "energy", scale = 41868000000, default = "GJ", }, ["tonTNT"] = { name2 = "tons of TNT", symbol = "ton of TNT", usename = 1, utype = "energy", scale = 4184000000, default = "GJ", link = "TNT equivalent", }, ["tTNT"] = { name2 = "tonnes of TNT", symbol = "tonne of TNT", usename = 1, utype = "energy", scale = 4184000000, default = "GJ", link = "TNT equivalent", }, ["TtonTNT"] = { name2 = "teratons of TNT", symbol = "teraton of TNT", usename = 1, utype = "energy", scale = 4.184e21, default = "ZJ", link = "TNT equivalent", }, ["TtTNT"] = { name2 = "teratonnes of TNT", symbol = "teratonne of TNT", usename = 1, utype = "energy", scale = 4.184e21, default = "ZJ", link = "TNT equivalent", }, ["TW.h"] = { name1 = "terawatt-hour", symbol = "TW⋅h", utype = "energy", scale = 3.6e15, default = "PJ", link = "Kilowatt-hour", }, ["TWh"] = { name1 = "terawatt-hour", symbol = "TWh", utype = "energy", scale = 3.6e15, default = "PJ", link = "Kilowatt-hour", }, ["W.h"] = { name1 = "watt-hour", symbol = "W⋅h", utype = "energy", scale = 3600, default = "kJ", link = "Kilowatt-hour", }, ["Wh"] = { name1 = "watt-hour", symbol = "Wh", utype = "energy", scale = 3600, default = "kJ", link = "Kilowatt-hour", }, ["μerg"] = { name1 = "microerg", symbol = "μerg", utype = "energy", scale = 1e-13, default = "nJ", link = "Erg", }, ["μeV"] = { name1 = "microelectronvolt", symbol = "μeV", utype = "energy", scale = 1.602176487e-25, default = "yJ", link = "Electronvolt", }, ["μW.h"] = { name1 = "microwatt-hour", symbol = "μW⋅h", utype = "energy", scale = 0.0036, default = "mJ", link = "Kilowatt-hour", }, ["μWh"] = { name1 = "microwatt-hour", symbol = "μWh", utype = "energy", scale = 0.0036, default = "mJ", link = "Kilowatt-hour", }, ["-kW.h"] = { target = "kW.h", link = "Kilowatt hour", }, ["btu"] = { target = "BTU", }, ["Calorie"] = { target = "Cal", }, ["ft.lbf"] = { target = "ftlbf", }, ["ft·lbf"] = { target = "ftlbf", }, ["g-cal-15"] = { target = "cal-15", }, ["g-cal-IT"] = { target = "cal-IT", }, ["g-cal-th"] = { target = "cal-th", }, ["g-kcal-15"] = { target = "kcal-15", }, ["g-kcal-IT"] = { target = "kcal-IT", }, ["g-kcal-th"] = { target = "kcal-th", }, ["g-Mcal-15"] = { target = "Mcal-15", }, ["g-mcal-15"] = { target = "mcal-15", }, ["g-Mcal-IT"] = { target = "Mcal-IT", }, ["g-mcal-IT"] = { target = "mcal-IT", }, ["g-Mcal-th"] = { target = "Mcal-th", }, ["g-mcal-th"] = { target = "mcal-th", }, ["GW-h"] = { target = "GW.h", }, ["GW·h"] = { target = "GW.h", }, ["Hartree"] = { target = "Eh", }, ["hp.h"] = { target = "hph", }, ["in.lb-f"] = { target = "inlb-f", }, ["in.lbf"] = { target = "inlbf", }, ["in.oz-f"] = { target = "inoz-f", }, ["in.ozf"] = { target = "inozf", }, ["kbboe"] = { target = "kBOE", symbol = "kbboe", }, ["kg-cal-15"] = { target = "Cal-15", }, ["kg-cal-IT"] = { target = "Cal-IT", }, ["kg-cal-th"] = { target = "Cal-th", }, ["kW-h"] = { target = "kW.h", }, ["kW·h"] = { target = "kW.h", }, ["MW-h"] = { target = "MW.h", }, ["mW-h"] = { target = "mW.h", }, ["MW·h"] = { target = "MW.h", }, ["TW-h"] = { target = "TW.h", }, ["uerg"] = { target = "μerg", }, ["ueV"] = { target = "μeV", }, ["uW-h"] = { target = "μW.h", }, ["uW.h"] = { target = "μW.h", }, ["uWh"] = { target = "μWh", }, ["W-h"] = { target = "W.h", }, ["eVpar"] = { _name1 = "electronvolt", _symbol = "eV", utype = "energy per chemical amount", scale = 96485.329522144166, prefixes = 1, default = "kcal/mol", link = "Electronvolt", }, ["kcal/mol"] = { per = { "kcal", "mol" }, utype = "energy per chemical amount", default = "kJ/mol", link = "Kilocalorie per mole", }, ["kJ/mol"] = { per = { "kJ", "mol" }, utype = "energy per chemical amount", default = "kcal/mol", link = "Joule per mole", }, ["kWh/100 km"] = { name1 = "kilowatt-hour per 100 kilometres", name1_us = "kilowatt-hour per 100 kilometers", name2 = "kilowatt-hours per 100 kilometres", name2_us = "kilowatt-hours per 100 kilometers", symbol = "kW⋅h/100&nbsp;km", utype = "energy per unit length", scale = 36, default = "MJ/km kWh/mi", link = "Kilowatt-hour", }, ["kWh/100 mi"] = { name1 = "kilowatt-hour per 100 miles", name2 = "kilowatt-hours per 100 miles", symbol = "kW⋅h/100&nbsp;mi", utype = "energy per unit length", scale = 22.3694, default = "mpge", link = "Miles per gallon gasoline equivalent", }, ["MJ/100 km"] = { name1 = "megajoule per 100 kilometres", name1_us = "megajoule per 100 kilometers", name2 = "megajoules per 100 kilometres", name2_us = "megajoules per 100 kilometers", symbol = "MJ/100&nbsp;km", utype = "energy per unit length", scale = 10, default = "BTU/mi", link = "British thermal unit", }, ["mpge"] = { name1 = "mile per gallon gasoline equivalent", name2 = "miles per gallon gasoline equivalent", symbol = "mpg&#8209;e", utype = "energy per unit length", scale = 1.3263314048360777e-5, invert = -1, iscomplex= true, default = "kWh/100 mi", link = "Miles per gallon gasoline equivalent", }, ["BTU/mi"] = { per = { "BTU", "mi" }, utype = "energy per unit length", default = "v > 1525 ! M ! k ! J/km", }, ["kJ/km"] = { per = { "kJ", "km" }, utype = "energy per unit length", default = "BTU/mi", }, ["kWh/km"] = { per = { "-kW.h", "km" }, utype = "energy per unit length", default = "MJ/km kWh/mi", }, ["kWh/mi"] = { per = { "-kW.h", "mi" }, utype = "energy per unit length", default = "kWh/km MJ/km", }, ["MJ/km"] = { per = { "MJ", "km" }, utype = "energy per unit length", default = "BTU/mi", }, ["mpg-e"] = { target = "mpge", }, ["BTU/lb"] = { name1 = "British thermal unit per pound", name2 = "British thermal units per pound", symbol = "BTU/lb", utype = "energy per unit mass", scale = 2326, default = "kJ/kg", link = "British thermal unit", }, ["cal/g"] = { name1 = "calorie per gram", name2 = "calories per gram", symbol = "cal/g", utype = "energy per unit mass", scale = 4184, default = "J/g", }, ["GJ/kg"] = { name1 = "gigajoule per kilogram", name2 = "gigajoules per kilogram", symbol = "GJ/kg", utype = "energy per unit mass", scale = 1e9, default = "ktTNT/t", link = "Specific energy", }, ["J/g"] = { name1 = "joule per gram", name2 = "joules per gram", symbol = "J/g", utype = "energy per unit mass", scale = 1000, default = "kcal/g", link = "Specific energy", }, ["kcal/g"] = { name1 = "kilocalorie per gram", name2 = "kilocalories per gram", symbol = "kcal/g", utype = "energy per unit mass", scale = 4184000, default = "kJ/g", }, ["kJ/g"] = { name1 = "kilojoule per gram", name2 = "kilojoules per gram", symbol = "kJ/g", utype = "energy per unit mass", scale = 1000000, default = "kcal/g", link = "Specific energy", }, ["kJ/kg"] = { name1 = "kilojoule per kilogram", name2 = "kilojoules per kilogram", symbol = "kJ/kg", utype = "energy per unit mass", scale = 1000, default = "BTU/lb", link = "Specific energy", }, ["ktonTNT/MT"] = { name2 = "kilotons of TNT per metric ton", symbol = "kiloton of TNT per metric ton", usename = 1, utype = "energy per unit mass", scale = 4184000000, default = "GJ/kg", link = "TNT equivalent", }, ["ktTNT/t"] = { name2 = "kilotonnes of TNT per tonne", symbol = "kilotonne of TNT per tonne", usename = 1, utype = "energy per unit mass", scale = 4184000000, default = "GJ/kg", link = "TNT equivalent", }, ["MtonTNT/MT"] = { name2 = "megatons of TNT per metric ton", symbol = "megaton of TNT per metric ton", usename = 1, utype = "energy per unit mass", scale = 4.184e12, default = "TJ/kg", link = "TNT equivalent", }, ["MtTNT/MT"] = { name2 = "megatonnes of TNT per tonne", symbol = "megatonne of TNT per tonne", usename = 1, utype = "energy per unit mass", scale = 4.184e12, default = "TJ/kg", link = "TNT equivalent", }, ["TJ/kg"] = { name1 = "terajoule per kilogram", name2 = "terajoules per kilogram", symbol = "TJ/kg", utype = "energy per unit mass", scale = 1e12, default = "MtTNT/MT", link = "Specific energy", }, ["Cal/g"] = { per = { "Cal", "g" }, utype = "energy per unit mass", default = "kJ/g", }, ["BTU/cuft"] = { per = { "BTU", "cuft" }, utype = "energy per unit volume", default = "kJ/L", }, ["Cal/12USoz(mL)serve"] = { per = { "Cal", "-12USoz(mL)serve" }, utype = "energy per unit volume", default = "kJ/L", }, ["Cal/12USoz(ml)serve"] = { per = { "Cal", "-12USoz(ml)serve" }, utype = "energy per unit volume", default = "kJ/l", }, ["Cal/12USozserve"] = { per = { "Cal", "-12USozserve" }, utype = "energy per unit volume", default = "kJ/L", }, ["Cal/USoz"] = { per = { "Cal", "USoz" }, utype = "energy per unit volume", default = "kJ/ml", }, ["kJ/L"] = { per = { "kJ", "L" }, utype = "energy per unit volume", default = "BTU/cuft", }, ["kJ/l"] = { per = { "kJ", "ll" }, utype = "energy per unit volume", default = "BTU/cuft", }, ["kJ/ml"] = { per = { "kJ", "ml" }, utype = "energy per unit volume", default = "Cal/USoz", }, ["MJ/m3"] = { per = { "MJ", "m3" }, utype = "energy per unit volume", default = "BTU/cuft", }, ["Sv"] = { _name1 = "sievert", _symbol = "Sv", utype = "equivalent radiation dose", scale = 1, prefixes = 1, default = "rem", link = "Sievert", }, ["rem"] = { _name1 = "rem", _symbol = "rem", utype = "equivalent radiation dose", scale = 0.01, prefixes = 1, default = "Sv", link = "Roentgen equivalent man", }, ["g/km"] = { name1 = "gram per kilometre", name1_us = "gram per kilometer", name2 = "grams per kilometre", name2_us = "grams per kilometer", symbol = "g/km", utype = "exhaust emission", scale = 1e-6, default = "oz/mi", link = "Exhaust gas", }, ["g/mi"] = { name1 = "gram per mile", name2 = "grams per mile", symbol = "g/mi", utype = "exhaust emission", scale = 6.2137119223733397e-7, default = "g/km", link = "Exhaust gas", }, ["gCO2/km"] = { name1 = "gram of CO<sub>2</sub> per kilometre", name1_us = "gram of CO<sub>2</sub> per kilometer", name2 = "grams of CO<sub>2</sub> per kilometre", name2_us = "grams of CO<sub>2</sub> per kilometer", symbol = "g(CO<sub>2</sub>)/km", utype = "exhaust emission", scale = 1e-6, default = "ozCO2/mi", link = "Exhaust gas", }, ["gCO2/mi"] = { name1 = "gram of CO<sub>2</sub> per mile", name2 = "grams of CO<sub>2</sub> per mile", symbol = "g(CO<sub>2</sub>)/mi", utype = "exhaust emission", scale = 6.2137119223733397e-7, default = "gCO2/km", link = "Exhaust gas", }, ["kg/km"] = { name1 = "kilogram per kilometre", name1_us = "kilogram per kilometer", name2 = "kilograms per kilometre", name2_us = "kilograms per kilometer", symbol = "kg/km", utype = "exhaust emission", scale = 0.001, default = "lb/mi", link = "Exhaust gas", }, ["kgCO2/km"] = { name1 = "kilogram of CO<sub>2</sub> per kilometre", name1_us = "kilogram of CO<sub>2</sub> per kilometer", name2 = "kilograms of CO<sub>2</sub> per kilometre", name2_us = "kilograms of CO<sub>2</sub> per kilometer", symbol = "kg(CO<sub>2</sub>)/km", utype = "exhaust emission", scale = 0.001, default = "lbCO2/mi", link = "Exhaust gas", }, ["lb/mi"] = { name1 = "pound per mile", name2 = "pounds per mile", symbol = "lb/mi", utype = "exhaust emission", scale = 0.00028184923173665794, default = "kg/km", link = "Exhaust gas", }, ["lbCO2/mi"] = { name1 = "pound of CO<sub>2</sub> per mile", name2 = "pounds of CO<sub>2</sub> per mile", symbol = "lb(CO<sub>2</sub>)/mi", utype = "exhaust emission", scale = 0.00028184923173665794, default = "kgCO2/km", link = "Exhaust gas", }, ["oz/mi"] = { name1 = "ounce per mile", name2 = "ounces per mile", symbol = "oz/mi", utype = "exhaust emission", scale = 1.7615576983541121e-5, default = "g/km", link = "Exhaust gas", }, ["ozCO2/mi"] = { name1 = "ounce of CO<sub>2</sub> per mile", name2 = "ounces of CO<sub>2</sub> per mile", symbol = "oz(CO<sub>2</sub>)/mi", utype = "exhaust emission", scale = 1.7615576983541121e-5, default = "gCO2/km", link = "Exhaust gas", }, ["cuft/a"] = { name1 = "cubic foot per annum", name2 = "cubic feet per annum", symbol = "cu&nbsp;ft/a", utype = "flow", scale = 8.9730672142368242e-10, default = "m3/a", link = "Cubic foot per second", }, ["cuft/d"] = { name1 = "cubic foot per day", name2 = "cubic feet per day", symbol = "cu&nbsp;ft/d", utype = "flow", scale = 3.2774128000000003e-7, default = "m3/d", link = "Cubic foot per second", }, ["cuft/h"] = { name1 = "cubic foot per hour", name2 = "cubic feet per hour", symbol = "cu&nbsp;ft/h", utype = "flow", scale = 7.8657907200000004e-6, default = "m3/h", link = "Cubic foot per second", }, ["cuft/min"] = { name1 = "cubic foot per minute", name2 = "cubic feet per minute", symbol = "cu&nbsp;ft/min", utype = "flow", scale = 0.00047194744319999999, default = "m3/min", }, ["cuft/s"] = { name1 = "cubic foot per second", name2 = "cubic feet per second", symbol = "cu&nbsp;ft/s", utype = "flow", scale = 28316846592e-12, default = "m3/s", }, ["cumi/a"] = { name1 = "cubic mile per annum", name2 = "cubic miles per annum", symbol = "cu&nbsp;mi/a", utype = "flow", scale = 132.08171170940057, default = "km3/a", link = "Cubic foot per second", }, ["cuyd/h"] = { name1 = "cubic yard per hour", name2 = "cubic yards per hour", symbol = "cuyd/h", utype = "flow", scale = 0.00021237634944000001, default = "m3/h", link = "Cubic foot per second", }, ["cuyd/s"] = { name1 = "cubic yard per second", name2 = "cubic yards per second", symbol = "cu&nbsp;yd/s", utype = "flow", scale = 0.76455485798400002, default = "m3/s", }, ["Goilbbl/a"] = { name1 = "billion barrels per year", name2 = "billion barrels per year", symbol = "Gbbl/a", utype = "flow", scale = 5.0380033629933836, default = "v * 1.58987294928 < 10 ! e6 ! e9 ! m3/a", link = "Barrel per day", }, ["impgal/h"] = { name1 = "imperial gallon per hour", name2 = "imperial gallons per hour", symbol = "imp&nbsp;gal/h", utype = "flow", scale = 1.2628027777777779e-6, default = "m3/h", link = "Gallon", }, ["impgal/min"] = { name1 = "imperial gallon per minute", name2 = "imperial gallons per minute", symbol = "imp gal/min", utype = "flow", scale = 7.5768166666666671e-5, default = "m3/s", link = "Gallon", }, ["impgal/s"] = { name1 = "imperial gallon per second", name2 = "imperial gallons per second", symbol = "impgal/s", utype = "flow", scale = 0.00454609, default = "m3/s", link = "Imperial gallons per second", }, ["km3/a"] = { name1 = "cubic kilometre per annum", name1_us = "cubic kilometer per annum", name2 = "cubic kilometres per annum", name2_us = "cubic kilometers per annum", symbol = "km<sup>3</sup>/a", utype = "flow", scale = 31.68808781402895, default = "cumi/a", link = "Cubic metre per second", }, ["km3/d"] = { name1 = "cubic kilometre per day", name1_us = "cubic kilometer per day", name2 = "cubic kilometres per day", name2_us = "cubic kilometers per day", symbol = "km<sup>3</sup>/d", utype = "flow", scale = 11574.074074074075, default = "cuft/d", link = "Cubic metre per second", }, ["koilbbl/a"] = { name1 = "thousand barrels per year", name2 = "thousand barrels per year", symbol = "kbbl/a", utype = "flow", scale = 5.0380033629933841e-6, default = "v * 1.58987294928 < 10 ! ! e3 ! m3/a", link = "Barrel per day", }, ["koilbbl/d"] = { name1 = "thousand barrels per day", name2 = "thousand barrels per day", symbol = "kbbl/d", utype = "flow", scale = 0.0018401307283333335, default = "v * 1.58987294928 < 10 ! ! e3 ! m3/d", link = "Barrel per day", }, ["L/h"] = { name1 = "litre per hour", name1_us = "liter per hour", name2 = "litres per hour", name2_us = "liters per hour", symbol = "L/h", utype = "flow", scale = 2.7777777777777776e-7, default = "impgal/h USgal/h", link = "Cubic metre per second", }, ["L/min"] = { name1 = "litre per minute", name1_us = "liter per minute", name2 = "litres per minute", name2_us = "liters per minute", symbol = "L/min", utype = "flow", scale = 1.6666666666666667e-5, default = "impgal/min USgal/min", link = "Cubic metre per second", }, ["L/s"] = { name1 = "litre per second", name1_us = "liter per second", name2 = "litres per second", name2_us = "liters per second", symbol = "L/s", utype = "flow", scale = 0.001, default = "cuft/s", link = "Cubic metre per second", }, ["m3/a"] = { name1 = "cubic metre per annum", name1_us = "cubic meter per annum", name2 = "cubic metres per annum", name2_us = "cubic meters per annum", symbol = "m<sup>3</sup>/a", utype = "flow", scale = 3.1688087814028947e-8, default = "cuft/a", link = "Cubic metre per second", }, ["m3/d"] = { name1 = "cubic metre per day", name1_us = "cubic meter per day", name2 = "cubic metres per day", name2_us = "cubic meters per day", symbol = "m<sup>3</sup>/d", utype = "flow", scale = 1.1574074074074073e-5, default = "cuft/d", link = "Cubic metre per second", }, ["m3/h"] = { name1 = "cubic metre per hour", name1_us = "cubic meter per hour", name2 = "cubic metres per hour", name2_us = "cubic meters per hour", symbol = "m<sup>3</sup>/h", utype = "flow", scale = 0.00027777777777777778, default = "cuft/h", link = "Cubic metre per second", }, ["m3/min"] = { name1 = "cubic metre per minute", name1_us = "cubic meter per minute", name2 = "cubic metres per minute", name2_us = "cubic meters per minute", symbol = "m<sup>3</sup>/min", utype = "flow", scale = 0.016666666666666666, default = "cuft/min", link = "Cubic metre per second", }, ["m3/s"] = { name1 = "cubic metre per second", name1_us = "cubic meter per second", name2 = "cubic metres per second", name2_us = "cubic meters per second", symbol = "m<sup>3</sup>/s", utype = "flow", scale = 1, default = "cuft/s", }, ["Moilbbl/a"] = { name1 = "million barrels per year", name2 = "million barrels per year", symbol = "Mbbl/a", utype = "flow", scale = 0.0050380033629933837, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3/a", link = "Barrel per day", }, ["Moilbbl/d"] = { name1 = "million barrels per day", name2 = "million barrels per day", symbol = "Mbbl/d", utype = "flow", scale = 1.8401307283333335, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3/d", link = "Barrel per day", }, ["oilbbl/a"] = { name1 = "barrel per year", name2 = "barrels per year", symbol = "bbl/a", utype = "flow", scale = 5.0380033629933841e-9, default = "m3/a", link = "Barrel per day", }, ["oilbbl/d"] = { name1 = "barrel per day", name2 = "barrels per day", symbol = "bbl/d", utype = "flow", scale = 1.8401307283333336e-6, default = "m3/d", }, ["Toilbbl/a"] = { name1 = "trillion barrels per year", name2 = "trillion barrels per year", symbol = "Tbbl/a", utype = "flow", scale = 5038.0033629933832, default = "v * 1.58987294928 < 10 ! e9 ! e12 ! m3/a", link = "Barrel per day", }, ["U.S.gal/d"] = { name1 = "U.S. gallon per day", name2 = "U.S. gallons per day", symbol = "U.S.&nbsp;gal/d", utype = "flow", scale = 4.3812636388888893e-8, default = "m3/s", customary= 1, }, ["U.S.gal/h"] = { name1 = "gallon per hour", name2 = "gallons per hour", symbol = "gal/h", utype = "flow", scale = 1.0515032733333334e-6, default = "m3/h", link = "Gallon", customary= 2, }, ["U.S.gal/min"] = { name1 = "U.S. gallon per minute", name2 = "U.S. gallons per minute", symbol = "U.S.&nbsp;gal/min", utype = "flow", scale = 6.3090196400000003e-5, default = "m3/s", link = "Gallon", }, ["USgal/a"] = { name1 = "US gallon per year", name2 = "US gallons per year", symbol = "US&nbsp;gal/a", utype = "flow", scale = 1.1995246102365199e-10, default = "m3/s", }, ["USgal/d"] = { name1 = "US gallon per day", name2 = "US gallons per day", symbol = "US&nbsp;gal/d", utype = "flow", scale = 4.3812636388888893e-8, default = "m3/s", }, ["USgal/h"] = { name1 = "gallon per hour", name2 = "gallons per hour", symbol = "gal/h", utype = "flow", scale = 1.0515032733333334e-6, default = "m3/h", link = "Gallon", customary= 1, }, ["USgal/min"] = { name1 = "US gallon per minute", name2 = "US gallons per minute", symbol = "US&nbsp;gal/min", utype = "flow", scale = 6.3090196400000003e-5, default = "m3/s", link = "Gallon", }, ["USgal/s"] = { name1 = "US gallon per second", name1_us = "U.S. gallon per second", name2 = "US gallons per second", name2_us = "U.S. gallons per second", symbol = "USgal/s", utype = "flow", scale = 0.003785411784, default = "m3/s", link = "US gallons per second", }, ["ft3/a"] = { target = "cuft/a", }, ["ft3/d"] = { target = "cuft/d", }, ["ft3/h"] = { target = "cuft/h", }, ["ft3/s"] = { target = "cuft/s", }, ["Gcuft/a"] = { target = "e9cuft/a", }, ["Gcuft/d"] = { target = "e9cuft/d", }, ["kcuft/a"] = { target = "e3cuft/a", }, ["kcuft/d"] = { target = "e3cuft/d", }, ["kcuft/s"] = { target = "e3cuft/s", }, ["Mcuft/a"] = { target = "e6cuft/a", }, ["Mcuft/d"] = { target = "e6cuft/d", }, ["Mcuft/s"] = { target = "e6cuft/s", }, ["m³/s"] = { target = "m3/s", }, ["Tcuft/a"] = { target = "e12cuft/a", }, ["Tcuft/d"] = { target = "e12cuft/d", }, ["u.s.gal/min"] = { target = "U.S.gal/min", }, ["usgal/min"] = { target = "USgal/min", }, ["-LTf"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "LTf", utype = "force", scale = 9964.01641818352, default = "kN", }, ["-STf"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "STf", utype = "force", scale = 8896.443230521, default = "kN", }, ["dyn"] = { name1 = "dyne", symbol = "dyn", utype = "force", scale = 0.00001, default = "gr-f", }, ["g-f"] = { name1 = "gram-force", name2 = "grams-force", symbol = "g<sub>f</sub>", utype = "force", scale = 0.00980665, default = "mN oz-f", link = "Kilogram-force", }, ["gf"] = { name1 = "gram-force", name2 = "grams-force", symbol = "gf", utype = "force", scale = 0.00980665, default = "mN ozf", link = "Kilogram-force", }, ["gr-f"] = { name1 = "grain-force", name2 = "grains-force", symbol = "gr<sub>f</sub>", utype = "force", scale = 0.0006354602307515, default = "μN", link = "Pound (force)", }, ["grf"] = { name1 = "grain-force", name2 = "grains-force", symbol = "grf", utype = "force", scale = 0.0006354602307515, default = "μN", link = "Pound (force)", }, ["kdyn"] = { name1 = "kilodyne", symbol = "kdyn", utype = "force", scale = 0.01, default = "oz-f", link = "Dyne", }, ["kg-f"] = { name1 = "kilogram-force", name2 = "kilograms-force", symbol = "kg<sub>f</sub>", utype = "force", scale = 9.80665, default = "N lb-f", }, ["kgf"] = { name1 = "kilogram-force", name2 = "kilograms-force", symbol = "kgf", utype = "force", scale = 9.80665, default = "N lbf", }, ["kp"] = { name1 = "kilopond", symbol = "kp", utype = "force", scale = 9.80665, default = "N lb-f", link = "Kilogram-force", }, ["L/T-f"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "L/T<sub>f</sub>", utype = "force", scale = 9964.01641818352, default = "kN", }, ["L/Tf"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "L/Tf", utype = "force", scale = 9964.01641818352, default = "kN", }, ["lb-f"] = { name1 = "pound-force", name2 = "pounds-force", symbol = "lb<sub>f</sub>", utype = "force", scale = 4.4482216152605, default = "N", link = "Pound (force)", }, ["lbf"] = { name1 = "pound-force", name2 = "pounds-force", symbol = "lbf", utype = "force", scale = 4.4482216152605, default = "N", link = "Pound (force)", }, ["lb(f)"] = { name1 = "pound", symbol = "lb", utype = "force", scale = 4.4482216152605, default = "N", link = "Pound (force)", }, ["LT-f"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "LT<sub>f</sub>", utype = "force", scale = 9964.01641818352, default = "kN", }, ["LTf"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "LTf", usename = 1, utype = "force", scale = 9964.01641818352, default = "kN", }, ["Mdyn"] = { name1 = "megadyne", symbol = "Mdyn", utype = "force", scale = 10, default = "lb-f", link = "Dyne", }, ["mdyn"] = { name1 = "millidyne", symbol = "mdyn", utype = "force", scale = 0.00000001, default = "gr-f", link = "Dyne", }, ["mg-f"] = { name1 = "milligram-force", name2 = "milligrams-force", symbol = "mg<sub>f</sub>", utype = "force", scale = 0.00000980665, default = "μN gr-f", link = "Kilogram-force", }, ["mgf"] = { name1 = "milligram-force", name2 = "milligrams-force", symbol = "mgf", utype = "force", scale = 0.00000980665, default = "μN grf", link = "Kilogram-force", }, ["Mp"] = { name1 = "megapond", symbol = "Mp", utype = "force", scale = 9806.65, default = "kN LT-f ST-f", link = "Kilogram-force", }, ["mp"] = { name1 = "millipond", symbol = "mp", utype = "force", scale = 0.00000980665, default = "μN gr-f", link = "Kilogram-force", }, ["N"] = { _name1 = "newton", _symbol = "N", utype = "force", scale = 1, prefixes = 1, default = "lb-f", link = "Newton (unit)", }, ["oz-f"] = { name1 = "ounce-force", name2 = "ounces-force", symbol = "oz<sub>f</sub>", utype = "force", scale = 0.2780138203095378125, default = "mN", link = "Pound (force)", }, ["ozf"] = { name1 = "ounce-force", name2 = "ounces-force", symbol = "ozf", utype = "force", scale = 0.2780138203095378125, default = "mN", link = "Pound (force)", }, ["p"] = { name1 = "pond", symbol = "p", utype = "force", scale = 0.00980665, default = "mN oz-f", link = "Kilogram-force", }, ["pdl"] = { name1 = "poundal", symbol = "pdl", utype = "force", scale = 0.138254954376, default = "N", }, ["S/T-f"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "S/T<sub>f</sub>", utype = "force", scale = 8896.443230521, default = "kN", }, ["S/Tf"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "S/Tf", utype = "force", scale = 8896.443230521, default = "kN", }, ["ST-f"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "ST<sub>f</sub>", utype = "force", scale = 8896.443230521, default = "kN", }, ["STf"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "STf", usename = 1, utype = "force", scale = 8896.443230521, default = "kN", }, ["t-f"] = { name1 = "tonne-force", name2 = "tonnes-force", symbol = "t<sub>f</sub>", utype = "force", scale = 9806.65, default = "kN LT-f ST-f", link = "Ton-force#Tonne-force", }, ["tf"] = { name1 = "tonne-force", name2 = "tonnes-force", symbol = "tf", utype = "force", scale = 9806.65, default = "kN LTf STf", link = "Ton-force#Tonne-force", }, ["dyne"] = { target = "dyn", }, ["newtons"] = { target = "N", }, ["poundal"] = { target = "pdl", }, ["tonne-force"] = { target = "tf", }, ["impgal/mi"] = { per = { "@impgal", "mi" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "L/km USgal/mi", }, ["km/L"] = { per = { "km", "L" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "mpgimp mpgus", }, ["km/l"] = { per = { "km", "ll" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "mpgimp mpgus", }, ["L/100 km"] = { per = { "L", "100km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", symlink = "[[Fuel economy in automobiles#Units of measure|L/100&nbsp;km]]", }, ["l/100 km"] = { per = { "ll", "100km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", symlink = "[[Fuel economy in automobiles#Units of measure|l/100&nbsp;km]]", }, ["L/km"] = { per = { "L", "km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", }, ["l/km"] = { per = { "ll", "km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", }, ["mi/impqt"] = { per = { "mi", "impqt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mi/U.S.qt"] = { per = { "mi", "U.S.qt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mi/USqt"] = { per = { "mi", "USqt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mi/usqt"] = { per = { "mi", "usqt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mpgimp"] = { per = { "mi", "@impgal" }, symbol = "mpg<sub>&#8209;imp</sub>", utype = "fuel efficiency", invert = -1, iscomplex= true, default = "L/100 km+mpgus", symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[Imperial units|imp]]</sub>", }, ["mpgus"] = { per = { "mi", "+USgal" }, symbol = "mpg<sub>&#8209;US</sub>", utype = "fuel efficiency", invert = -1, iscomplex= true, default = "L/100 km+mpgimp", symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[United States customary units|US]]</sub>", }, ["U.S.gal/mi"] = { per = { "*U.S.gal", "mi" }, sp_us = true, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "L/km impgal/mi", }, ["usgal/mi"] = { per = { "+USgal", "mi" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "L/km impgal/mi", }, ["L/100km"] = { target = "L/100 km", }, ["l/100km"] = { target = "l/100 km", }, ["mpg"] = { shouldbe = "Use %{mpgus%} for miles per US gallon or %{mpgimp%} for miles per imperial gallon (not %{mpg%})", }, ["mpgU.S."] = { target = "mpgus", symbol = "mpg<sub>&#8209;U.S.</sub>", sp_us = true, symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[United States customary units|U.S.]]</sub>", }, ["mpgu.s."] = { target = "mpgus", symbol = "mpg<sub>&#8209;U.S.</sub>", sp_us = true, symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[United States customary units|U.S.]]</sub>", }, ["mpgUS"] = { target = "mpgus", }, ["USgal/mi"] = { target = "usgal/mi", }, ["kPa/m"] = { per = { "kPa", "-m-frac" }, utype = "fracture gradient", default = "psi/ft", }, ["psi/ft"] = { per = { "psi", "-ft-frac" }, utype = "fracture gradient", default = "kPa/m", }, ["cm/km"] = { name1 = "centimetre per kilometre", name1_us = "centimeter per kilometer", name2 = "centimetres per kilometre", name2_us = "centimeters per kilometer", symbol = "cm/km", utype = "gradient", scale = 0.00001, default = "ft/mi", link = "Grade (slope)", }, ["ft/mi"] = { name1 = "foot per mile", name2 = "feet per mile", symbol = "ft/mi", utype = "gradient", scale = 0.00018939393939393939, default = "v < 5.28 ! c ! ! m/km", link = "Grade (slope)", }, ["ft/nmi"] = { name1 = "foot per nautical mile", name2 = "feet per nautical mile", symbol = "ft/nmi", utype = "gradient", scale = 0.00016457883369330455, default = "v < 6.076 ! c ! ! m/km", link = "Grade (slope)", }, ["in/ft"] = { name1 = "inch per foot", name2 = "inches per foot", symbol = "in/ft", utype = "gradient", scale = 0.083333333333333329, default = "mm/m", link = "Grade (slope)", }, ["in/mi"] = { name1 = "inch per mile", name2 = "inches per mile", symbol = "in/mi", utype = "gradient", scale = 1.5782828282828283e-5, default = "v < 0.6336 ! m ! c ! m/km", link = "Grade (slope)", }, ["m/km"] = { name1 = "metre per kilometre", name1_us = "meter per kilometer", name2 = "metres per kilometre", name2_us = "meters per kilometer", symbol = "m/km", utype = "gradient", scale = 0.001, default = "ft/mi", link = "Grade (slope)", }, ["mm/km"] = { name1 = "millimetre per kilometre", name1_us = "millimeter per kilometer", name2 = "millimetres per kilometre", name2_us = "millimeters per kilometer", symbol = "mm/km", utype = "gradient", scale = 0.000001, default = "in/mi", link = "Grade (slope)", }, ["mm/m"] = { name1 = "millimetre per metre", name1_us = "millimeter per meter", name2 = "millimetres per metre", name2_us = "millimeters per meter", symbol = "mm/m", utype = "gradient", scale = 0.001, default = "in/ft", link = "Grade (slope)", }, ["admi"] = { name1 = "admiralty mile", symbol = "nmi&nbsp;(admiralty)", utype = "length", scale = 1853.184, default = "km mi", link = "Nautical mile", }, ["AU"] = { name1 = "astronomical unit", symbol = "AU", utype = "length", scale = 149597870700, default = "km mi", }, ["Brnmi"] = { name1 = "British nautical mile", symbol = "(Brit)&nbsp;nmi", utype = "length", scale = 1853.184, default = "km mi", link = "Nautical mile", }, ["bu"] = { name2 = "bu", symbol = "bu", usename = 1, utype = "length", scale = 0.0030303030303030303, default = "mm", link = "Japanese units of measurement#Length", }, ["ch"] = { name1 = "chain", symbol = "ch", utype = "length", scale = 20.1168, default = "ft m", subdivs = { ["ft"] = { 66, default = "m" }, ["yd"] = { 22, default = "m" } }, link = "Chain (unit)", }, ["chlk"] = { name1 = "[[Chain (unit)|chain]]", symbol = "[[Chain (unit)|ch]]", utype = "length", scale = 20.1168, default = "ft m", link = "", }, ["chain"] = { symbol = "chain", usename = 1, utype = "length", scale = 20.1168, default = "ft m", subdivs = { ["ft"] = { 66, default = "m" }, ["yd"] = { 22, default = "m" } }, link = "Chain (unit)", }, ["chainlk"] = { symbol = "[[Chain (unit)|chain]]", usename = 1, utype = "length", scale = 20.1168, default = "ft m", link = "", }, ["dpcm"] = { name2 = "dot/cm", symbol = "dot/cm", utype = "length", scale = 100, invert = -1, iscomplex= true, default = "dpi", link = "Dots per inch", }, ["dpi"] = { name2 = "DPI", symbol = "DPI", utype = "length", scale = 39.370078740157481, invert = -1, iscomplex= true, default = "pitch", link = "Dots per inch", }, ["fathom"] = { symbol = "fathom", usename = 1, utype = "length", scale = 1.8288, default = "ft m", }, ["foot"] = { name1 = "foot", name2 = "foot", symbol = "ft", utype = "length", scale = 0.3048, default = "m", subdivs = { ["in"] = { 12, default = "m" } }, link = "Foot (unit)", }, ["ft"] = { name1 = "foot", name2 = "feet", symbol = "ft", utype = "length", scale = 0.3048, exception= "integer_more_precision", default = "m", subdivs = { ["in"] = { 12, default = "m" } }, link = "Foot (unit)", }, ["furlong"] = { symbol = "furlong", usename = 1, utype = "length", scale = 201.168, default = "ft m", }, ["Gly"] = { name1 = "gigalight-year", symbol = "Gly", utype = "length", scale = 9.4607304725808e24, default = "Mpc", link = "Light-year#Definitions", }, ["Gpc"] = { name1 = "gigaparsec", symbol = "Gpc", utype = "length", scale = 3.0856775814671916e25, default = "Gly", link = "Parsec#Megaparsecs and gigaparsecs", }, ["hand"] = { name1 = "hand", symbol = "h", utype = "length", builtin = "hand", scale = 0.1016, iscomplex= true, default = "in cm", link = "Hand (unit)", }, ["in"] = { name1 = "inch", name2 = "inches", symbol = "in", utype = "length", scale = 0.0254, exception= "subunit_more_precision", default = "mm", }, ["inabbreviated"] = { name2 = "in", symbol = "in", utype = "length", scale = 0.0254, default = "mm", link = "Inch", }, ["kly"] = { name1 = "kilolight-year", symbol = "kly", utype = "length", scale = 9.4607304725808e18, default = "pc", link = "Light-year#Definitions", }, ["kpc"] = { name1 = "kiloparsec", symbol = "kpc", utype = "length", scale = 3.0856775814671916e19, default = "kly", link = "Parsec#Parsecs and kiloparsecs", }, ["LD"] = { name1 = "lunar distance", symbol = "LD", utype = "length", scale = 384403000, default = "km mi", link = "Lunar distance (astronomy)", }, ["league"] = { symbol = "league", usename = 1, utype = "length", scale = 4828.032, default = "km", link = "League (unit)", }, ["ly"] = { name1 = "light-year", symbol = "ly", utype = "length", scale = 9.4607304725808e15, default = "AU", }, ["m"] = { _name1 = "metre", _name1_us= "meter", _symbol = "m", utype = "length", scale = 1, prefixes = 1, default = "v > 0 and v < 3 ! ftin ! ft", link = "Metre", }, ["mi"] = { name1 = "mile", symbol = "mi", utype = "length", scale = 1609.344, default = "km", subdivs = { ["ch"] = { 80, default = "km" }, ["chlk"] = { 80, default = "km" }, ["chain"] = { 80, default = "km" }, ["chainlk"] = { 80, default = "km" }, ["ft"] = { 5280, default = "km" }, ["furlong"] = { 8, default = "km" }, ["yd"] = { 1760, default = "km" } }, }, ["mil"] = { symbol = "mil", usename = 1, utype = "length", scale = 0.0000254, default = "mm", link = "Thousandth of an inch", }, ["Mly"] = { name1 = "megalight-year", symbol = "Mly", utype = "length", scale = 9.4607304725808e21, default = "kpc", link = "Light-year#Definitions", }, ["Mpc"] = { name1 = "megaparsec", symbol = "Mpc", utype = "length", scale = 3.0856775814671916e22, default = "Mly", link = "Parsec#Megaparsecs and gigaparsecs", }, ["NM"] = { name1 = "nautical mile", symbol = "NM", utype = "length", scale = 1852, default = "km mi", }, ["nmi"] = { name1 = "nautical mile", symbol = "nmi", utype = "length", scale = 1852, default = "km mi", }, ["oldUKnmi"] = { name1 = "nautical mile", symbol = "nmi", utype = "length", scale = 1853.184, default = "km mi", }, ["oldUSnmi"] = { name1 = "nautical mile", symbol = "nmi", utype = "length", scale = 1853.24496, default = "km mi", }, ["pc"] = { name1 = "parsec", symbol = "pc", utype = "length", scale = 3.0856775814671916e16, default = "ly", }, ["perch"] = { name2 = "perches", symbol = "perch", usename = 1, utype = "length", scale = 5.0292, default = "ft m", link = "Rod (unit)", }, ["pitch"] = { name2 = "μm", symbol = "μm", utype = "length", scale = 1e-6, default = "dpi", defkey = "pitch", linkey = "pitch", link = "Dots per inch", }, ["pole"] = { symbol = "pole", usename = 1, utype = "length", scale = 5.0292, default = "ft m", link = "Rod (unit)", }, ["pre1954U.S.nmi"] = { name1 = "(pre-1954&nbsp;U.S.) nautical mile", symbol = "(pre&#8209;1954&nbsp;U.S.) nmi", utype = "length", scale = 1853.24496, default = "km mi", link = "Nautical mile", }, ["pre1954USnmi"] = { name1 = "(pre-1954&nbsp;US) nautical mile", name1_us = "(pre-1954&nbsp;U.S.) nautical mile", symbol = "(pre&#8209;1954&nbsp;US) nmi", sym_us = "(pre&#8209;1954&nbsp;U.S.) nmi", utype = "length", scale = 1853.24496, default = "km mi", link = "Nautical mile", }, ["rd"] = { name1 = "rod", symbol = "rd", utype = "length", scale = 5.0292, default = "ft m", link = "Rod (unit)", }, ["royal cubit"] = { name1 = "royal cubit", symbol = "cu", utype = "length", scale = 0.524, default = "mm", }, ["rtkm"] = { name1 = "route kilometre", name1_us = "route kilometer", symbol = "km", utype = "length", scale = 1000, default = "mi", link = "Kilometre", }, ["rtmi"] = { name1 = "route mile", symbol = "mi", utype = "length", scale = 1609.344, default = "km", link = "Mile", }, ["shaku"] = { name2 = "shaku", symbol = "shaku", usename = 1, utype = "length", scale = 0.30303030303030304, default = "m", link = "Shaku (unit)", }, ["sm"] = { name1 = "smoot", symbol = "sm", utype = "length", scale = 1.70180, default = "m", link = "Smoot (unit)", }, ["smi"] = { name1 = "statute mile", symbol = "mi", utype = "length", scale = 1609.344, default = "km", subdivs = { ["chain"] = { 80, default = "km" } }, }, ["solar radius"] = { name1 = "solar radius", name2 = "solar radii", symbol = "''R''<sub>☉</sub>", utype = "length", scale = 695700e3, default = "km", }, ["sun"] = { name2 = "sun", symbol = "sun", usename = 1, utype = "length", scale = 0.030303030303030304, default = "mm", link = "Japanese units of measurement#Length", }, ["thou"] = { name2 = "thou", symbol = "thou", usename = 1, utype = "length", scale = 0.0000254, default = "mm", link = "Thousandth of an inch", }, ["verst"] = { symbol = "verst", usename = 1, utype = "length", scale = 1066.8, default = "km mi", }, ["yd"] = { name1 = "yard", symbol = "yd", utype = "length", scale = 0.9144, default = "m", subdivs = { ["ft"] = { 3, default = "m" } }, }, ["μin"] = { name1 = "microinch", name2 = "microinches", symbol = "μin", utype = "length", scale = 0.0000000254, default = "nm", link = "SI prefix#Non-metric units", }, ["Å"] = { name1 = "ångström", symbol = "Å", utype = "length", scale = 0.0000000001, default = "in", }, ["Hz"] = { _name1 = "hertz", _name2 = "hertz", _symbol = "Hz", utype = "length", scale = 3.3356409519815204e-9, invert = -1, iscomplex= true, prefixes = 1, default = "m", link = "Hertz", }, ["rpm"] = { name1 = "revolution per minute", name2 = "revolutions per minute", symbol = "rpm", utype = "length", scale = 5.5594015866358675e-11, invert = -1, iscomplex= true, default = "Hz", link = "Revolutions per minute", }, ["-ft-frac"] = { target = "ft", link = "Fracture gradient", }, ["-in-stiff"] = { target = "in", link = "Stiffness", }, ["-m-frac"] = { target = "m", link = "Fracture gradient", }, ["-m-stiff"] = { target = "m", link = "Stiffness", }, ["100km"] = { target = "km", multiplier= 100, }, ["100mi"] = { target = "mi", multiplier= 100, }, ["100miles"] = { target = "mi", symbol = "miles", multiplier= 100, }, ["admiralty nmi"] = { target = "oldUKnmi", }, ["angstrom"] = { target = "Å", }, ["au"] = { target = "AU", symbol = "au", }, ["feet"] = { target = "ft", }, ["hands"] = { target = "hand", }, ["inch"] = { target = "in", }, ["inches"] = { target = "in", }, ["light-year"] = { target = "ly", }, ["meter"] = { target = "m", sp_us = true, }, ["meters"] = { target = "m", sp_us = true, }, ["metre"] = { target = "m", }, ["metres"] = { target = "m", }, ["micrometre"] = { target = "μm", }, ["micron"] = { target = "μm", default = "μin", }, ["mile"] = { target = "mi", }, ["miles"] = { target = "mi", }, ["parsec"] = { target = "pc", }, ["rod"] = { target = "rd", }, ["smoot"] = { target = "sm", }, ["uin"] = { target = "μin", }, ["yard"] = { target = "yd", }, ["yards"] = { target = "yd", }, ["yds"] = { target = "yd", }, ["dtex"] = { name1 = "decitex", name2 = "decitex", symbol = "dtex", utype = "linear density", scale = 1e-7, default = "lb/yd", link = "Units of textile measurement#Units", }, ["kg/cm"] = { name1 = "kilogram per centimetre", name1_us = "kilogram per centimeter", name2 = "kilograms per centimetre", name2_us = "kilograms per centimeter", symbol = "kg/cm", utype = "linear density", scale = 100, default = "lb/yd", link = "Linear density", }, ["kg/m"] = { name1 = "kilogram per metre", name1_us = "kilogram per meter", name2 = "kilograms per metre", name2_us = "kilograms per meter", symbol = "kg/m", utype = "linear density", scale = 1, default = "lb/yd", link = "Linear density", }, ["lb/ft"] = { name1 = "pound per foot", name2 = "pounds per foot", symbol = "lb/ft", utype = "linear density", scale = 1.4881639435695539, default = "kg/m", link = "Linear density", }, ["lb/yd"] = { name1 = "pound per yard", name2 = "pounds per yard", symbol = "lb/yd", utype = "linear density", scale = 0.49605464785651798, default = "kg/m", link = "Linear density", }, ["G"] = { _name1 = "gauss", _name2 = "gauss", _symbol = "G", utype = "magnetic field strength", scale = 0.0001, prefixes = 1, default = "T", link = "Gauss (unit)", }, ["T"] = { _name1 = "tesla", _symbol = "T", utype = "magnetic field strength", scale = 1, prefixes = 1, default = "G", link = "Tesla (unit)", }, ["A/m"] = { name1 = "ampere per metre", name1_us = "ampere per meter", name2 = "amperes per metre", name2_us = "amperes per meter", symbol = "A/m", utype = "magnetizing field", scale = 1, default = "Oe", }, ["kA/m"] = { name1 = "kiloampere per metre", name1_us = "kiloampere per meter", name2 = "kiloamperes per metre", name2_us = "kiloamperes per meter", symbol = "kA/m", utype = "magnetizing field", scale = 1000, default = "kOe", link = "Ampere per metre", }, ["MA/m"] = { name1 = "megaampere per metre", name1_us = "megaampere per meter", name2 = "megaamperes per metre", name2_us = "megaamperes per meter", symbol = "MA/m", utype = "magnetizing field", scale = 1e6, default = "kOe", link = "Ampere per metre", }, ["Oe"] = { _name1 = "oersted", _symbol = "Oe", utype = "magnetizing field", scale = 79.5774715, prefixes = 1, default = "kA/m", link = "Oersted", }, ["-Lcwt"] = { name1 = "hundredweight", name2 = "hundredweight", symbol = "cwt", utype = "mass", scale = 50.80234544, default = "lb", }, ["-Scwt"] = { name1 = "hundredweight", name2 = "hundredweight", symbol = "cwt", utype = "mass", scale = 45.359237, default = "lb", }, ["-ST"] = { name1 = "short ton", symbol = "ST", utype = "mass", scale = 907.18474, default = "t", }, ["carat"] = { symbol = "carat", usename = 1, utype = "mass", scale = 0.0002, default = "g", link = "Carat (mass)", }, ["drachm"] = { name1_us = "dram", symbol = "drachm", usename = 1, utype = "mass", scale = 0.001771845195, default = "g", link = "Dram (unit)", }, ["dram"] = { target = "drachm", }, ["dwt"] = { name1 = "pennyweight", symbol = "dwt", utype = "mass", scale = 0.00155517384, default = "oz g", }, ["DWton"] = { symbol = "deadweight ton", usename = 1, utype = "mass", scale = 1016.0469088, default = "DWtonne", link = "Deadweight tonnage", }, ["DWtonne"] = { name1_us = "deadweight metric ton", symbol = "deadweight tonne", sym_us = "~deadweight metric ton", usename = 1, utype = "mass", scale = 1000, default = "DWton", link = "Deadweight tonnage", }, ["g"] = { _name1 = "gram", _symbol = "g", utype = "mass", scale = 0.001, prefixes = 1, default = "oz", link = "Gram", }, ["gr"] = { name1 = "grain", symbol = "gr", utype = "mass", scale = 0.00006479891, default = "g", link = "Grain (unit)", }, ["Gt"] = { name1 = "gigatonne", symbol = "Gt", utype = "mass", scale = 1000000000000, default = "LT ST", link = "Tonne", }, ["impgalh2o"] = { name1 = "imperial gallon of water", name2 = "imperial gallons of water", symbol = "imp&nbsp;gal H<sub>2</sub>O", utype = "mass", scale = 4.5359236999999499, default = "lb kg", link = "Imperial gallon", }, ["kt"] = { name1 = "kilotonne", symbol = "kt", utype = "mass", scale = 1000000, default = "LT ST", link = "Tonne", }, ["lb"] = { name1 = "pound", symbol = "lb", utype = "mass", scale = 0.45359237, exception= "integer_more_precision", default = "kg", subdivs = { ["oz"] = { 16, default = "kg" } }, link = "Pound (mass)", }, ["Lcwt"] = { name1 = "long hundredweight", name2 = "long hundredweight", symbol = "Lcwt", usename = 1, utype = "mass", scale = 50.80234544, default = "lb", subdivs = { ["qtr"] = { 4, default = "kg" }, ["st"] = { 8, default = "kg" } }, link = "Hundredweight", }, ["long cwt"] = { name1 = "long hundredweight", name2 = "long hundredweight", symbol = "long&nbsp;cwt", utype = "mass", scale = 50.80234544, default = "lb kg", subdivs = { ["qtr"] = { 4, default = "kg" } }, link = "Hundredweight", }, ["long qtr"] = { name1 = "long quarter", symbol = "long&nbsp;qtr", utype = "mass", scale = 12.70058636, default = "lb kg", }, ["LT"] = { symbol = "long ton", usename = 1, utype = "mass", scale = 1016.0469088, default = "t", subdivs = { ["Lcwt"] = { 20, default = "t", unit = "-Lcwt" } }, }, ["lt"] = { name1 = "long ton", symbol = "LT", utype = "mass", scale = 1016.0469088, default = "t", subdivs = { ["Lcwt"] = { 20, default = "t", unit = "-Lcwt" } }, }, ["metric ton"] = { symbol = "metric ton", usename = 1, utype = "mass", scale = 1000, default = "long ton", link = "Tonne", }, ["MT"] = { name1 = "metric ton", symbol = "t", utype = "mass", scale = 1000, default = "LT ST", link = "Tonne", }, ["Mt"] = { name1 = "megatonne", symbol = "Mt", utype = "mass", scale = 1000000000, default = "LT ST", link = "Tonne", }, ["oz"] = { name1 = "ounce", symbol = "oz", utype = "mass", scale = 0.028349523125, default = "g", }, ["ozt"] = { name1 = "troy ounce", symbol = "ozt", utype = "mass", scale = 0.0311034768, default = "oz g", }, ["pdr"] = { name1 = "pounder", symbol = "pdr", utype = "mass", scale = 0.45359237, default = "kg", link = "Pound (mass)", }, ["qtr"] = { name1 = "quarter", symbol = "qtr", utype = "mass", scale = 12.70058636, default = "lb kg", subdivs = { ["lb"] = { 28, default = "kg" } }, link = "Long quarter", }, ["Scwt"] = { name1 = "short hundredweight", name2 = "short hundredweight", symbol = "Scwt", usename = 1, utype = "mass", scale = 45.359237, default = "lb", link = "Hundredweight", }, ["short cwt"] = { name1 = "short hundredweight", name2 = "short hundredweight", symbol = "short&nbsp;cwt", utype = "mass", scale = 45.359237, default = "lb kg", link = "Hundredweight", }, ["short qtr"] = { name1 = "short quarter", symbol = "short&nbsp;qtr", utype = "mass", scale = 11.33980925, default = "lb kg", }, ["ST"] = { symbol = "short ton", usename = 1, utype = "mass", scale = 907.18474, default = "t", subdivs = { ["Scwt"] = { 20, default = "t", unit = "-Scwt" } }, }, ["shtn"] = { name1 = "short ton", symbol = "sh&nbsp;tn", utype = "mass", scale = 907.18474, default = "t", }, ["shton"] = { symbol = "ton", usename = 1, utype = "mass", scale = 907.18474, default = "t", }, ["solar mass"] = { name1 = "solar mass", name2 = "solar masses", symbol = "''M''<sub>☉</sub>", utype = "mass", scale = 1.98855e30, default = "kg", }, ["st"] = { name1 = "stone", name2 = "stone", symbol = "st", utype = "mass", scale = 6.35029318, default = "lb kg", subdivs = { ["lb"] = { 14, default = "kg lb" } }, link = "Stone (unit)", }, ["t"] = { name1 = "tonne", name1_us = "metric ton", symbol = "t", utype = "mass", scale = 1000, default = "LT ST", }, ["tonne"] = { name1 = "tonne", name1_us = "metric ton", symbol = "t", utype = "mass", scale = 1000, default = "shton", }, ["troy pound"] = { symbol = "troy pound", usename = 1, utype = "mass", scale = 0.3732417216, default = "lb kg", link = "Troy weight", }, ["usgalh2o"] = { name1 = "US gallon of water", name1_us = "U.S. gallon of water", name2 = "US gallons of water", name2_us = "U.S. gallons of water", symbol = "US&nbsp;gal H<sub>2</sub>O", utype = "mass", scale = 3.7776215836051126, default = "lb kg", link = "United States customary units#Fluid volume", }, ["viss"] = { name2 = "viss", symbol = "viss", utype = "mass", scale = 1.632932532, default = "kg", link = "Myanmar units of measurement#Mass", }, ["billion tonne"] = { target = "e9t", }, ["kilogram"] = { target = "kg", }, ["kilotonne"] = { target = "kt", }, ["lbs"] = { target = "lb", }, ["lbt"] = { target = "troy pound", }, ["lcwt"] = { target = "Lcwt", }, ["long ton"] = { target = "LT", }, ["mcg"] = { target = "μg", }, ["million tonne"] = { target = "e6t", }, ["scwt"] = { target = "Scwt", }, ["short ton"] = { target = "ST", }, ["stone"] = { target = "st", }, ["thousand tonne"] = { target = "e3t", }, ["tonnes"] = { target = "t", }, ["kg/kW"] = { name1 = "kilogram per kilowatt", name2 = "kilograms per kilowatt", symbol = "kg/kW", utype = "mass per unit power", scale = 0.001, default = "lb/hp", link = "Kilowatt", }, ["lb/hp"] = { name1 = "pound per horsepower", name2 = "pounds per horsepower", symbol = "lb/hp", utype = "mass per unit power", scale = 0.00060827738784176115, default = "kg/kW", link = "Horsepower", }, ["kg/h"] = { per = { "kg", "h" }, utype = "mass per unit time", default = "lb/h", }, ["lb/h"] = { per = { "lb", "h" }, utype = "mass per unit time", default = "kg/h", }, ["g-mol/d"] = { name1 = "gram-mole per day", name2 = "gram-moles per day", symbol = "g&#8209;mol/d", utype = "molar rate", scale = 1.1574074074074073e-5, default = "μmol/s", link = "Mole (unit)", }, ["g-mol/h"] = { name1 = "gram-mole per hour", name2 = "gram-moles per hour", symbol = "g&#8209;mol/h", utype = "molar rate", scale = 0.00027777777777777778, default = "mmol/s", link = "Mole (unit)", }, ["g-mol/min"] = { name1 = "gram-mole per minute", name2 = "gram-moles per minute", symbol = "g&#8209;mol/min", utype = "molar rate", scale = 0.016666666666666666, default = "g-mol/s", link = "Mole (unit)", }, ["g-mol/s"] = { name1 = "gram-mole per second", name2 = "gram-moles per second", symbol = "g&#8209;mol/s", utype = "molar rate", scale = 1, default = "lb-mol/min", link = "Mole (unit)", }, ["gmol/d"] = { name1 = "gram-mole per day", name2 = "gram-moles per day", symbol = "gmol/d", utype = "molar rate", scale = 1.1574074074074073e-5, default = "μmol/s", link = "Mole (unit)", }, ["gmol/h"] = { name1 = "gram-mole per hour", name2 = "gram-moles per hour", symbol = "gmol/h", utype = "molar rate", scale = 0.00027777777777777778, default = "mmol/s", link = "Mole (unit)", }, ["gmol/min"] = { name1 = "gram-mole per minute", name2 = "gram-moles per minute", symbol = "gmol/min", utype = "molar rate", scale = 0.016666666666666666, default = "gmol/s", link = "Mole (unit)", }, ["gmol/s"] = { name1 = "gram-mole per second", name2 = "gram-moles per second", symbol = "gmol/s", utype = "molar rate", scale = 1, default = "lbmol/min", link = "Mole (unit)", }, ["kmol/d"] = { name1 = "kilomole per day", name2 = "kilomoles per day", symbol = "kmol/d", utype = "molar rate", scale = 0.011574074074074073, default = "mmol/s", link = "Mole (unit)", }, ["kmol/h"] = { name1 = "kilomole per hour", name2 = "kilomoles per hour", symbol = "kmol/h", utype = "molar rate", scale = 0.27777777777777779, default = "mol/s", link = "Mole (unit)", }, ["kmol/min"] = { name1 = "kilomole per minute", name2 = "kilomoles per minute", symbol = "kmol/min", utype = "molar rate", scale = 16.666666666666668, default = "mol/s", link = "Kilomole (unit)", }, ["kmol/s"] = { name1 = "kilomole per second", name2 = "kilomoles per second", symbol = "kmol/s", utype = "molar rate", scale = 1000, default = "lb-mol/s", link = "Mole (unit)", }, ["lb-mol/d"] = { name1 = "pound-mole per day", name2 = "pound-moles per day", symbol = "lb&#8209;mol/d", utype = "molar rate", scale = 0.0052499116898148141, default = "mmol/s", link = "Pound-mole", }, ["lb-mol/h"] = { name1 = "pound-mole per hour", name2 = "pound-moles per hour", symbol = "lb&#8209;mol/h", utype = "molar rate", scale = 0.12599788055555555, default = "mol/s", link = "Pound-mole", }, ["lb-mol/min"] = { name1 = "pound-mole per minute", name2 = "pound-moles per minute", symbol = "lb&#8209;mol/min", utype = "molar rate", scale = 7.5598728333333334, default = "mol/s", link = "Pound-mole", }, ["lb-mol/s"] = { name1 = "pound-mole per second", name2 = "pound-moles per second", symbol = "lb&#8209;mol/s", utype = "molar rate", scale = 453.59237, default = "kmol/s", link = "Pound-mole", }, ["lbmol/d"] = { name1 = "pound-mole per day", name2 = "pound-moles per day", symbol = "lbmol/d", utype = "molar rate", scale = 0.0052499116898148141, default = "mmol/s", link = "Pound-mole", }, ["lbmol/h"] = { name1 = "pound-mole per hour", name2 = "pound-moles per hour", symbol = "lbmol/h", utype = "molar rate", scale = 0.12599788055555555, default = "mol/s", link = "Pound-mole", }, ["lbmol/min"] = { name1 = "pound-mole per minute", name2 = "pound-moles per minute", symbol = "lbmol/min", utype = "molar rate", scale = 7.5598728333333334, default = "mol/s", link = "Pound-mole", }, ["lbmol/s"] = { name1 = "pound-mole per second", name2 = "pound-moles per second", symbol = "lbmol/s", utype = "molar rate", scale = 453.59237, default = "kmol/s", link = "Pound-mole", }, ["mmol/s"] = { name1 = "millimole per second", name2 = "millimoles per second", symbol = "mmol/s", utype = "molar rate", scale = 0.001, default = "lb-mol/d", link = "Mole (unit)", }, ["mol/d"] = { name1 = "mole per day", name2 = "moles per day", symbol = "mol/d", utype = "molar rate", scale = 1.1574074074074073e-5, default = "μmol/s", link = "Mole (unit)", }, ["mol/h"] = { name1 = "mole per hour", name2 = "moles per hour", symbol = "mol/h", utype = "molar rate", scale = 0.00027777777777777778, default = "mmol/s", link = "Mole (unit)", }, ["mol/min"] = { name1 = "mole per minute", name2 = "moles per minute", symbol = "mol/min", utype = "molar rate", scale = 0.016666666666666666, default = "mol/s", link = "Mole (unit)", }, ["mol/s"] = { name1 = "mole per second", name2 = "moles per second", symbol = "mol/s", utype = "molar rate", scale = 1, default = "lb-mol/min", link = "Mole (unit)", }, ["μmol/s"] = { name1 = "micromole per second", name2 = "micromoles per second", symbol = "μmol/s", utype = "molar rate", scale = 0.000001, default = "lb-mol/d", link = "Mole (unit)", }, ["umol/s"] = { target = "μmol/s", }, ["/acre"] = { name1 = "per acre", name2 = "per acre", symbol = "/acre", utype = "per unit area", scale = 0.00024710538146716532, default = "/ha", link = "Acre", }, ["/ha"] = { name1 = "per hectare", name2 = "per hectare", symbol = "/ha", utype = "per unit area", scale = 100e-6, default = "/acre", link = "Hectare", }, ["/sqcm"] = { name1 = "per square centimetre", name1_us = "per square centimeter", name2 = "per square centimetre", name2_us = "per square centimeter", symbol = "/cm<sup>2</sup>", utype = "per unit area", scale = 1e4, default = "/sqin", link = "Square centimetre", }, ["/sqin"] = { name1 = "per square inch", name2 = "per square inch", symbol = "/in<sup>2</sup>", utype = "per unit area", scale = 1550.0031000062002, default = "/sqcm", link = "Square inch", }, ["/sqkm"] = { name1 = "per square kilometre", name1_us = "per square kilometer", name2 = "per square kilometre", name2_us = "per square kilometer", symbol = "/km<sup>2</sup>", utype = "per unit area", scale = 1e-6, default = "/sqmi", link = "Square kilometre", }, ["/sqmi"] = { name1 = "per square mile", name2 = "per square mile", symbol = "/sq&nbsp;mi", utype = "per unit area", scale = 3.8610215854244582e-7, default = "/sqkm", link = "Square mile", }, ["PD/acre"] = { name1 = "inhabitant per acre", name2 = "inhabitants per acre", symbol = "/acre", utype = "per unit area", scale = 0.00024710538146716532, default = "PD/ha", link = "Acre", }, ["PD/ha"] = { name1 = "inhabitant per hectare", name2 = "inhabitants per hectare", symbol = "/ha", utype = "per unit area", scale = 100e-6, default = "PD/acre", link = "Hectare", }, ["PD/sqkm"] = { name1 = "inhabitant per square kilometre", name1_us = "inhabitant per square kilometer", name2 = "inhabitants per square kilometre", name2_us = "inhabitants per square kilometer", symbol = "/km<sup>2</sup>", utype = "per unit area", scale = 1e-6, default = "PD/sqmi", link = "Square kilometre", }, ["PD/sqmi"] = { name1 = "inhabitant per square mile", name2 = "inhabitants per square mile", symbol = "/sq&nbsp;mi", utype = "per unit area", scale = 3.8610215854244582e-7, default = "PD/sqkm", link = "Square mile", }, ["/cm2"] = { target = "/sqcm", }, ["/in2"] = { target = "/sqin", }, ["/km2"] = { target = "/sqkm", }, ["pd/acre"] = { target = "PD/acre", }, ["pd/ha"] = { target = "PD/ha", }, ["PD/km2"] = { target = "PD/sqkm", }, ["pd/km2"] = { target = "PD/sqkm", }, ["PD/km²"] = { target = "PD/sqkm", }, ["pd/sqkm"] = { target = "PD/sqkm", }, ["pd/sqmi"] = { target = "PD/sqmi", }, ["/l"] = { name1 = "per litre", name1_us = "per liter", name2 = "per litre", name2_us = "per liter", symbol = "/l", utype = "per unit volume", scale = 1000, default = "/usgal", link = "Litre", }, ["/L"] = { name1 = "per litre", name1_us = "per liter", name2 = "per litre", name2_us = "per liter", symbol = "/L", utype = "per unit volume", scale = 1000, default = "/usgal", link = "Litre", }, ["/USgal"] = { name1 = "per gallon", name2 = "per gallon", symbol = "/gal", utype = "per unit volume", scale = 264.172052, default = "/L", link = "US gallon", customary= 2, }, ["/usgal"] = { target = "/USgal", }, ["bhp"] = { name1 = "brake horsepower", name2 = "brake horsepower", symbol = "bhp", utype = "power", scale = 745.69987158227022, default = "kW", link = "Horsepower#Brake horsepower", }, ["Cal/d"] = { name1 = "large calorie per day", name2 = "large calories per day", symbol = "Cal/d", utype = "power", scale = 0.048425925925925928, default = "kJ/d", link = "Calorie", }, ["Cal/h"] = { name1 = "large calorie per hour", name2 = "large calories per hour", symbol = "Cal/h", utype = "power", scale = 1.1622222222222223, default = "kJ/h", link = "Calorie", }, ["cal/h"] = { name1 = "calorie per hour", name2 = "calories per hour", symbol = "cal/h", utype = "power", scale = 0.0011622222222222223, default = "W", link = "Calorie", }, ["CV"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "CV", utype = "power", scale = 735.49875, default = "kW", }, ["hk"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "hk", utype = "power", scale = 735.49875, default = "kW", }, ["hp"] = { name1 = "horsepower", name2 = "horsepower", symbol = "hp", utype = "power", scale = 745.69987158227022, default = "kW", }, ["hp-electric"] = { name1 = "electric horsepower", name2 = "electric horsepower", symbol = "hp", utype = "power", scale = 746, default = "kW", link = "Horsepower#Electrical horsepower", }, ["hp-electrical"] = { name1 = "electrical horsepower", name2 = "electrical horsepower", symbol = "hp", utype = "power", scale = 746, default = "kW", link = "Horsepower#Electrical horsepower", }, ["hp-metric"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "hp", utype = "power", scale = 735.49875, default = "kW", }, ["ihp"] = { name1 = "indicated horsepower", name2 = "indicated horsepower", symbol = "ihp", utype = "power", scale = 745.69987158227022, default = "kW", link = "Horsepower#Indicated horsepower", }, ["kcal/h"] = { name1 = "kilocalorie per hour", name2 = "kilocalories per hour", symbol = "kcal/h", utype = "power", scale = 1.1622222222222223, default = "kW", link = "Calorie", }, ["kJ/d"] = { name1 = "kilojoule per day", name2 = "kilojoules per day", symbol = "kJ/d", utype = "power", scale = 0.011574074074074073, default = "Cal/d", link = "Kilojoule", }, ["kJ/h"] = { name1 = "kilojoule per hour", name2 = "kilojoules per hour", symbol = "kJ/h", utype = "power", scale = 0.27777777777777779, default = "W", link = "Kilojoule", }, ["PS"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "PS", utype = "power", scale = 735.49875, default = "kW", }, ["shp"] = { name1 = "shaft horsepower", name2 = "shaft horsepower", symbol = "shp", utype = "power", scale = 745.69987158227022, default = "kW", link = "Horsepower#Shaft horsepower", }, ["W"] = { _name1 = "watt", _symbol = "W", utype = "power", scale = 1, prefixes = 1, default = "hp", link = "Watt", }, ["BTU/h"] = { per = { "BTU", "h" }, utype = "power", default = "W", }, ["Btu/h"] = { per = { "Btu", "h" }, utype = "power", default = "W", }, ["BHP"] = { target = "bhp", }, ["btu/h"] = { target = "BTU/h", }, ["HP"] = { target = "hp", }, ["Hp"] = { target = "hp", }, ["hp-mechanical"] = { target = "hp", }, ["IHP"] = { target = "ihp", }, ["SHP"] = { target = "shp", }, ["whp"] = { target = "hp", }, ["hp/lb"] = { name1 = "horsepower per pound", name2 = "horsepower per pound", symbol = "hp/lb", utype = "power per unit mass", scale = 1643.986806, default = "kW/kg", link = "Power-to-weight ratio", }, ["hp/LT"] = { name1 = "horsepower per long ton", name2 = "horsepower per long ton", symbol = "hp/LT", utype = "power per unit mass", scale = 0.73392268125000004, default = "kW/t", link = "Power-to-weight ratio", }, ["hp/ST"] = { name1 = "horsepower per short ton", name2 = "horsepower per short ton", symbol = "hp/ST", utype = "power per unit mass", scale = 0.821993403, default = "kW/t", link = "Power-to-weight ratio", }, ["hp/t"] = { name1 = "horsepower per tonne", name2 = "horsepower per tonne", symbol = "hp/t", utype = "power per unit mass", scale = 0.74569987158227022, default = "kW/t", link = "Power-to-weight ratio", }, ["kW/kg"] = { name1 = "kilowatt per kilogram", name2 = "kilowatts per kilogram", symbol = "kW/kg", utype = "power per unit mass", scale = 1000, default = "hp/lb", link = "Power-to-weight ratio", }, ["kW/t"] = { name1 = "kilowatt per tonne", name2 = "kilowatts per tonne", symbol = "kW/t", utype = "power per unit mass", scale = 1, default = "PS/t", link = "Power-to-weight ratio", }, ["PS/t"] = { name1 = "metric horsepower per tonne", name2 = "metric horsepower per tonne", symbol = "PS/t", utype = "power per unit mass", scale = 0.73549875, default = "kW/t", link = "Power-to-weight ratio", }, ["shp/lb"] = { name1 = "shaft horsepower per pound", name2 = "shaft horsepower per pound", symbol = "shp/lb", utype = "power per unit mass", scale = 1643.986806, default = "kW/kg", link = "Power-to-weight ratio", }, ["hp/tonne"] = { target = "hp/t", symbol = "hp/tonne", default = "kW/tonne", }, ["kW/tonne"] = { target = "kW/t", symbol = "kW/tonne", }, ["-lb/in2"] = { name1 = "pound per square inch", name2 = "pounds per square inch", symbol = "lb/in<sup>2</sup>", utype = "pressure", scale = 6894.7572931683608, default = "kPa kgf/cm2", }, ["atm"] = { name1 = "standard atmosphere", symbol = "atm", utype = "pressure", scale = 101325, default = "kPa", link = "Atmosphere (unit)", }, ["Ba"] = { name1 = "barye", symbol = "Ba", utype = "pressure", scale = 0.1, default = "Pa", }, ["bar"] = { symbol = "bar", utype = "pressure", scale = 100000, default = "kPa", link = "Bar (unit)", }, ["dbar"] = { name1 = "decibar", symbol = "dbar", utype = "pressure", scale = 10000, default = "kPa", link = "Bar (unit)", }, ["inHg"] = { name1 = "inch of mercury", name2 = "inches of mercury", symbol = "inHg", utype = "pressure", scale = 3386.388640341, default = "kPa", }, ["kBa"] = { name1 = "kilobarye", symbol = "kBa", utype = "pressure", scale = 100, default = "hPa", link = "Barye", }, ["kg-f/cm2"] = { name1 = "kilogram-force per square centimetre", name1_us = "kilogram-force per square centimeter", name2 = "kilograms-force per square centimetre", name2_us = "kilograms-force per square centimeter", symbol = "kg<sub>f</sub>/cm<sup>2</sup>", utype = "pressure", scale = 98066.5, default = "psi", link = "Kilogram-force", }, ["kg/cm2"] = { name1 = "kilogram per square centimetre", name1_us = "kilogram per square centimeter", name2 = "kilograms per square centimetre", name2_us = "kilograms per square centimeter", symbol = "kg/cm<sup>2</sup>", utype = "pressure", scale = 98066.5, default = "psi", link = "Kilogram-force", }, ["kgf/cm2"] = { name1 = "kilogram-force per square centimetre", name1_us = "kilogram-force per square centimeter", name2 = "kilograms-force per square centimetre", name2_us = "kilograms-force per square centimeter", symbol = "kgf/cm<sup>2</sup>", utype = "pressure", scale = 98066.5, default = "psi", link = "Kilogram-force", }, ["ksi"] = { name1 = "kilopound per square inch", name2 = "kilopounds per square inch", symbol = "ksi", utype = "pressure", scale = 6894757.2931683613, default = "MPa", link = "Pound per square inch", }, ["lbf/in2"] = { name1 = "pound-force per square inch", name2 = "pounds-force per square inch", symbol = "lbf/in<sup>2</sup>", utype = "pressure", scale = 6894.7572931683608, default = "kPa kgf/cm2", }, ["mb"] = { name1 = "millibar", symbol = "mb", utype = "pressure", scale = 100, default = "hPa", link = "Bar (unit)", }, ["mbar"] = { name1 = "millibar", symbol = "mbar", utype = "pressure", scale = 100, default = "hPa", link = "Bar (unit)", }, ["mmHg"] = { name1 = "millimetre of mercury", name1_us = "millimeter of mercury", name2 = "millimetres of mercury", name2_us = "millimeters of mercury", symbol = "mmHg", utype = "pressure", scale = 133.322387415, default = "kPa", }, ["Pa"] = { _name1 = "pascal", _symbol = "Pa", utype = "pressure", scale = 1, prefixes = 1, default = "psi", link = "Pascal (unit)", }, ["psf"] = { name1 = "pound per square foot", name2 = "pounds per square foot", symbol = "psf", utype = "pressure", scale = 47.880258980335839, default = "kPa", link = "Pound per square inch", }, ["psi"] = { name1 = "pound per square inch", name2 = "pounds per square inch", symbol = "psi", utype = "pressure", scale = 6894.7572931683608, default = "kPa", }, ["Torr"] = { name1 = "torr", symbol = "Torr", utype = "pressure", scale = 133.32236842105263, default = "kPa", }, ["N/cm2"] = { per = { "N", "cm2" }, utype = "pressure", default = "psi", }, ["N/m2"] = { per = { "N", "m2" }, utype = "pressure", default = "psi", }, ["g/cm2"] = { per = { "g", "cm2" }, utype = "pressure", default = "lb/sqft", multiplier= 9.80665, }, ["g/m2"] = { per = { "g", "m2" }, utype = "pressure", default = "lb/sqft", multiplier= 9.80665, }, ["kg/ha"] = { per = { "kg", "ha" }, utype = "pressure", default = "lb/acre", multiplier= 9.80665, }, ["kg/m2"] = { per = { "kg", "m2" }, utype = "pressure", default = "lb/sqft", multiplier= 9.80665, }, ["lb/1000sqft"] = { per = { "lb", "1000sqft" }, utype = "pressure", default = "g/m2", multiplier= 9.80665, }, ["lb/acre"] = { per = { "lb", "acre" }, utype = "pressure", default = "kg/ha", multiplier= 9.80665, }, ["lb/sqft"] = { per = { "lb", "sqft" }, utype = "pressure", default = "kg/m2", multiplier= 9.80665, }, ["lb/sqyd"] = { per = { "lb", "sqyd" }, utype = "pressure", default = "kg/m2", multiplier= 9.80665, }, ["LT/acre"] = { per = { "LT", "acre" }, utype = "pressure", default = "t/ha", multiplier= 9.80665, }, ["MT/ha"] = { per = { "MT", "ha" }, utype = "pressure", default = "LT/acre ST/acre", multiplier= 9.80665, }, ["oz/sqft"] = { per = { "oz", "sqft" }, utype = "pressure", default = "g/m2", multiplier= 9.80665, }, ["oz/sqyd"] = { per = { "oz", "sqyd" }, utype = "pressure", default = "g/m2", multiplier= 9.80665, }, ["ST/acre"] = { per = { "ST", "acre" }, utype = "pressure", default = "t/ha", multiplier= 9.80665, }, ["t/ha"] = { per = { "t", "ha" }, utype = "pressure", default = "LT/acre ST/acre", multiplier= 9.80665, }, ["tonne/acre"] = { per = { "tonne", "acre" }, utype = "pressure", default = "tonne/ha", multiplier= 9.80665, }, ["tonne/ha"] = { per = { "tonne", "ha" }, utype = "pressure", default = "tonne/acre", multiplier= 9.80665, }, ["kgfpsqcm"] = { target = "kgf/cm2", }, ["kgpsqcm"] = { target = "kg/cm2", }, ["kN/m2"] = { target = "kPa", }, ["lb/in2"] = { target = "lbf/in2", }, ["torr"] = { target = "Torr", }, ["Bq"] = { _name1 = "becquerel", _symbol = "Bq", utype = "radioactivity", scale = 1, prefixes = 1, default = "pCi", link = "Becquerel", }, ["Ci"] = { _name1 = "curie", _symbol = "Ci", utype = "radioactivity", scale = 3.7e10, prefixes = 1, default = "GBq", link = "Curie (unit)", }, ["Rd"] = { _name1 = "rutherford", _symbol = "Rd", utype = "radioactivity", scale = 1e6, prefixes = 1, default = "MBq", link = "Rutherford (unit)", }, ["cm/h"] = { name1 = "centimetre per hour", name1_us = "centimeter per hour", name2 = "centimetres per hour", name2_us = "centimeters per hour", symbol = "cm/h", utype = "speed", scale = 2.7777777777777775e-6, default = "in/h", link = "Metre per second", }, ["cm/s"] = { name1 = "centimetre per second", name1_us = "centimeter per second", name2 = "centimetres per second", name2_us = "centimeters per second", symbol = "cm/s", utype = "speed", scale = 0.01, default = "in/s", link = "Metre per second", }, ["cm/year"] = { name1 = "centimetre per year", name1_us = "centimeter per year", name2 = "centimetres per year", name2_us = "centimeters per year", symbol = "cm/year", utype = "speed", scale = 3.168873850681143e-10, default = "in/year", link = "Orders of magnitude (speed)", }, ["foot/s"] = { name1 = "foot per second", name2 = "foot per second", symbol = "ft/s", utype = "speed", scale = 0.3048, default = "m/s", }, ["ft/min"] = { name1 = "foot per minute", name2 = "feet per minute", symbol = "ft/min", utype = "speed", scale = 0.00508, default = "m/min", link = "Feet per second", }, ["ft/s"] = { name1 = "foot per second", name2 = "feet per second", symbol = "ft/s", utype = "speed", scale = 0.3048, default = "m/s", link = "Feet per second", }, ["furlong per fortnight"] = { name2 = "furlongs per fortnight", symbol = "furlong per fortnight", usename = 1, utype = "speed", scale = 0.00016630952380952381, default = "km/h mph", link = "FFF system", }, ["in/h"] = { name1 = "inch per hour", name2 = "inches per hour", symbol = "in/h", utype = "speed", scale = 7.0555555555555559e-6, default = "cm/h", link = "Inch", }, ["in/s"] = { name1 = "inch per second", name2 = "inches per second", symbol = "in/s", utype = "speed", scale = 0.0254, default = "cm/s", link = "Inch", }, ["in/year"] = { name1 = "inch per year", name2 = "inches per year", symbol = "in/year", utype = "speed", scale = 8.0489395807301024e-10, default = "cm/year", link = "Orders of magnitude (speed)", }, ["isp"] = { name1 = "second", symbol = "s", utype = "speed", scale = 9.80665, default = "km/s", link = "Specific impulse", }, ["km/d"] = { name1 = "kilometre per day", name1_us = "kilometer per day", name2 = "kilometres per day", name2_us = "kilometers per day", symbol = "km/d", utype = "speed", scale = 1.1574074074074074e-2, default = "mi/d", link = "Orders of magnitude (speed)", }, ["km/h"] = { name1 = "kilometre per hour", name1_us = "kilometer per hour", name2 = "kilometres per hour", name2_us = "kilometers per hour", symbol = "km/h", utype = "speed", scale = 0.27777777777777779, default = "mph", link = "Kilometres per hour", }, ["km/s"] = { name1 = "kilometre per second", name1_us = "kilometer per second", name2 = "kilometres per second", name2_us = "kilometers per second", symbol = "km/s", utype = "speed", scale = 1000, default = "mi/s", link = "Metre per second", }, ["kn"] = { name1 = "knot", symbol = "kn", utype = "speed", scale = 0.51444444444444448, default = "km/h mph", link = "Knot (unit)", }, ["kNs/kg"] = { name2 = "kN&#8209;s/kg", symbol = "kN&#8209;s/kg", utype = "speed", scale = 1000, default = "isp", link = "Specific impulse", }, ["m/min"] = { name1 = "metre per minute", name1_us = "meter per minute", name2 = "metres per minute", name2_us = "meters per minute", symbol = "m/min", utype = "speed", scale = 0.016666666666666666, default = "ft/min", link = "Metre per second", }, ["m/s"] = { name1 = "metre per second", name1_us = "meter per second", name2 = "metres per second", name2_us = "meters per second", symbol = "m/s", utype = "speed", scale = 1, default = "ft/s", }, ["Mach"] = { name2 = "Mach", symbol = "Mach", utype = "speed", builtin = "mach", scale = 0, iscomplex= true, default = "km/h mph", link = "Mach number", }, ["mi/d"] = { name1 = "mile per day", name2 = "miles per day", symbol = "mi/d", utype = "speed", scale = 1.8626666666666667e-2, default = "km/d", link = "Orders of magnitude (speed)", }, ["mi/s"] = { name1 = "mile per second", name2 = "miles per second", symbol = "mi/s", utype = "speed", scale = 1609.344, default = "km/s", link = "Mile", }, ["mm/h"] = { name1 = "millimetre per hour", name1_us = "millimeter per hour", name2 = "millimetres per hour", name2_us = "millimeters per hour", symbol = "mm/h", utype = "speed", scale = 2.7777777777777781e-7, default = "in/h", link = "Metre per second", }, ["mph"] = { name1 = "mile per hour", name2 = "miles per hour", symbol = "mph", utype = "speed", scale = 0.44704, default = "km/h", link = "Miles per hour", }, ["Ns/kg"] = { name2 = "N&#8209;s/kg", symbol = "N&#8209;s/kg", utype = "speed", scale = 1, default = "isp", link = "Specific impulse", }, ["si tsfc"] = { name2 = "g/(kN⋅s)", symbol = "g/(kN⋅s)", utype = "speed", scale = 9.9999628621379242e-7, invert = -1, iscomplex= true, default = "tsfc", link = "Thrust specific fuel consumption", }, ["tsfc"] = { name2 = "lb/(lbf⋅h)", symbol = "lb/(lbf⋅h)", utype = "speed", scale = 2.832545036049801e-5, invert = -1, iscomplex= true, default = "si tsfc", link = "Thrust specific fuel consumption", }, ["cm/y"] = { target = "cm/year", }, ["cm/yr"] = { target = "cm/year", }, ["in/y"] = { target = "in/year", }, ["in/yr"] = { target = "in/year", }, ["knot"] = { target = "kn", }, ["knots"] = { target = "kn", }, ["kph"] = { target = "km/h", }, ["mi/h"] = { target = "mph", }, ["mm/s"] = { per = { "mm", "s" }, utype = "speed", default = "in/s", link = "Metre per second", }, ["C"] = { name1 = "degree Celsius", name2 = "degrees Celsius", symbol = "°C", usesymbol= 1, utype = "temperature", scale = 1, offset = -273.15, iscomplex= true, istemperature= true, default = "F", link = "Celsius", }, ["F"] = { name1 = "degree Fahrenheit", name2 = "degrees Fahrenheit", symbol = "°F", usesymbol= 1, utype = "temperature", scale = 0.55555555555555558, offset = 32-273.15*(9/5), iscomplex= true, istemperature= true, default = "C", link = "Fahrenheit", }, ["K"] = { _name1 = "kelvin", _symbol = "K", usesymbol= 1, utype = "temperature", scale = 1, offset = 0, iscomplex= true, istemperature= true, prefixes = 1, default = "C F", link = "Kelvin", }, ["keVT"] = { name1 = "kiloelectronvolt", symbol = "keV", utype = "temperature", scale = 11.604505e6, offset = 0, iscomplex= true, default = "MK", link = "Electronvolt", }, ["R"] = { name1 = "degree Rankine", name2 = "degrees Rankine", symbol = "°R", usesymbol= 1, utype = "temperature", scale = 0.55555555555555558, offset = 0, iscomplex= true, istemperature= true, default = "K F C", link = "Rankine scale", }, ["Celsius"] = { target = "C", }, ["°C"] = { target = "C", }, ["°F"] = { target = "F", }, ["°R"] = { target = "R", }, ["C-change"] = { name1 = "degree Celsius change", name2 = "degrees Celsius change", symbol = "°C", usesymbol= 1, utype = "temperature change", scale = 1, default = "F-change", link = "Celsius", }, ["F-change"] = { name1 = "degree Fahrenheit change", name2 = "degrees Fahrenheit change", symbol = "°F", usesymbol= 1, utype = "temperature change", scale = 0.55555555555555558, default = "C-change", link = "Fahrenheit", }, ["K-change"] = { name1 = "kelvin change", name2 = "kelvins change", symbol = "K", usesymbol= 1, utype = "temperature change", scale = 1, default = "F-change", link = "Kelvin", }, ["°C-change"] = { target = "C-change", }, ["°F-change"] = { target = "F-change", }, ["century"] = { name1 = "century", name2 = "centuries", symbol = "ha", utype = "time", scale = 3155760000, default = "Gs", }, ["d"] = { name1 = "day", symbol = "d", utype = "time", scale = 86400, default = "ks", }, ["decade"] = { name1 = "decade", symbol = "daa", utype = "time", scale = 315576000, default = "Ms", }, ["dog year"] = { name1 = "dog year", symbol = "dog yr", utype = "time", scale = 220903200, default = "years", link = "List of unusual units of measurement#Dog year", }, ["fortnight"] = { symbol = "fortnight", usename = 1, utype = "time", scale = 1209600, default = "week", }, ["h"] = { name1 = "hour", symbol = "h", utype = "time", scale = 3600, default = "ks", }, ["long billion year"] = { name1 = "billion years", name2 = "billion years", symbol = "Ta", utype = "time", scale = 31557600000000000000, default = "Es", link = "Annum", }, ["millennium"] = { name1 = "millennium", name2 = "millennia", symbol = "ka", utype = "time", scale = 31557600000, default = "Gs", }, ["milliard year"] = { name1 = "milliard years", name2 = "milliard years", symbol = "Ga", utype = "time", scale = 31557600000000000, default = "Ps", link = "Annum", }, ["million year"] = { name1 = "million years", name2 = "million years", symbol = "Ma", utype = "time", scale = 31557600000000, default = "Ts", link = "Annum", }, ["min"] = { name1 = "minute", symbol = "min", utype = "time", scale = 60, default = "s", }, ["month"] = { symbol = "month", usename = 1, utype = "time", scale = 2629800, default = "Ms", }, ["months"] = { name1 = "month", symbol = "mo", utype = "time", scale = 2629800, default = "year", }, ["s"] = { _name1 = "second", _symbol = "s", utype = "time", scale = 1, prefixes = 1, default = "v < 7200 ! min ! h", link = "Second", }, ["short billion year"] = { name1 = "billion years", name2 = "billion years", symbol = "Ga", utype = "time", scale = 31557600000000000, default = "Ps", link = "Annum", }, ["short trillion year"] = { name1 = "trillion years", name2 = "trillion years", symbol = "Ta", utype = "time", scale = 31557600000000000000, default = "Es", link = "Annum", }, ["thousand million year"] = { name1 = "thousand million years", name2 = "thousand million years", symbol = "Ga", utype = "time", scale = 31557600000000000, default = "Ps", link = "Annum", }, ["wk"] = { symbol = "week", usename = 1, utype = "time", scale = 604800, default = "Ms", }, ["year"] = { name1 = "year", symbol = "a", utype = "time", scale = 31557600, default = "Ms", link = "Annum", }, ["years"] = { name1 = "year", symbol = "yr", utype = "time", scale = 31557600, default = "Ms", link = "Annum", }, ["byr"] = { target = "short billion year", }, ["day"] = { target = "d", }, ["days"] = { target = "d", }, ["dog yr"] = { target = "dog year", }, ["Gyr"] = { target = "thousand million year", }, ["hour"] = { target = "h", }, ["hours"] = { target = "h", }, ["kMyr"] = { target = "thousand million year", }, ["kmyr"] = { target = "thousand million year", }, ["kyr"] = { target = "millennium", }, ["long byr"] = { target = "long billion year", }, ["minute"] = { target = "min", }, ["minutes"] = { target = "min", }, ["mth"] = { target = "month", }, ["Myr"] = { target = "million year", }, ["myr"] = { target = "million year", }, ["sec"] = { target = "s", }, ["second"] = { target = "s", }, ["seconds"] = { target = "s", }, ["tmyr"] = { target = "thousand million year", }, ["tryr"] = { target = "short trillion year", }, ["tyr"] = { target = "millennium", }, ["week"] = { target = "wk", }, ["weeks"] = { target = "wk", }, ["yr"] = { target = "year", }, ["kg.m"] = { name1 = "kilogram metre", name1_us = "kilogram meter", symbol = "kg⋅m", utype = "torque", scale = 9.80665, default = "Nm lbft", link = "Kilogram metre (torque)", }, ["kgf.m"] = { name1 = "kilogram force-metre", name1_us = "kilogram force-meter", symbol = "kgf⋅m", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["kgm"] = { name1 = "kilogram metre", name1_us = "kilogram meter", symbol = "kg⋅m", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["kpm"] = { name1 = "kilopond metre", name1_us = "kilopond meter", symbol = "kp⋅m", utype = "torque", scale = 9.80665, default = "Nm lbft", link = "Kilogram metre (torque)", }, ["lb-fft"] = { name1 = "pound force-foot", name2 = "pound force-feet", symbol = "ft⋅lb<sub>f</sub>", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["lb.ft"] = { name1 = "pound force-foot", name2 = "pound force-feet", symbol = "lb⋅ft", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["lb.in"] = { name1 = "pound force-inch", symbol = "lb⋅in", utype = "torque", scale = 0.1129848290276167, default = "mN.m", link = "Pound-foot (torque)", }, ["lbfft"] = { name1 = "pound force-foot", name2 = "pound force-feet", symbol = "lbf⋅ft", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["lbft"] = { name1 = "pound-foot", name2 = "pound-feet", symbol = "lb⋅ft", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["m.kg-f"] = { name1 = "metre kilogram-force", name1_us = "meter kilogram-force", name2 = "metre kilograms-force", name2_us = "meter kilograms-force", symbol = "m⋅kg<sub>f</sub>", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["m.kgf"] = { name1 = "metre kilogram-force", name1_us = "meter kilogram-force", name2 = "metre kilograms-force", name2_us = "meter kilograms-force", symbol = "m⋅kgf", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["mN.m"] = { name1 = "millinewton-metre", name1_us = "millinewton-meter", symbol = "mN⋅m", utype = "torque", scale = 0.001, default = "lb.in", link = "Newton-metre", }, ["Nm"] = { _name1 = "newton-metre", _name1_us= "newton-meter", _symbol = "N⋅m", utype = "torque", alttype = "energy", scale = 1, prefixes = 1, default = "lbfft", link = "Newton-metre", }, ["kN/m"] = { per = { "kN", "-m-stiff" }, utype = "torque", default = "lbf/in", }, ["lbf/in"] = { per = { "lbf", "-in-stiff" }, utype = "torque", default = "kN/m", }, ["lb-f.ft"] = { target = "lb-fft", }, ["lbf.ft"] = { target = "lbfft", }, ["lbf·ft"] = { target = "lbfft", }, ["lb·ft"] = { target = "lb.ft", }, ["mkg-f"] = { target = "m.kg-f", }, ["mkgf"] = { target = "m.kgf", }, ["N.m"] = { target = "Nm", }, ["N·m"] = { target = "Nm", }, ["ton-mile"] = { symbol = "ton-mile", usename = 1, utype = "transportation", scale = 1.4599723182105602, default = "tkm", }, ["tkm"] = { name1 = "tonne-kilometre", name1_us = "tonne-kilometer", symbol = "tkm", utype = "transportation", scale = 1, default = "ton-mile", }, ["-12USoz(mL)serve"] = { name1_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;mL) serving", symbol = "12&nbsp;US&nbsp;fl&nbsp;oz (355&nbsp;mL) serving", sym_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;mL) serving", utype = "volume", scale = 0.00035488235475000004, default = "mL", link = "Beverage can#Standard sizes", }, ["-12USoz(ml)serve"] = { name1_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;ml) serving", symbol = "12&nbsp;US&nbsp;fl&nbsp;oz (355&nbsp;ml) serving", sym_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;ml) serving", utype = "volume", scale = 0.00035488235475000004, default = "ml", link = "Beverage can#Standard sizes", }, ["-12USozserve"] = { name1_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz serving", symbol = "12&nbsp;US&nbsp;fl&nbsp;oz serving", sym_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz serving", utype = "volume", scale = 0.00035488235475000004, default = "mL", link = "Beverage can#Standard sizes", }, ["acre-foot"] = { name1 = "acre-foot", name2 = "acre-foot", symbol = "acre⋅ft", utype = "volume", scale = 1233.48183754752, default = "m3", }, ["acre-ft"] = { name1 = "acre-foot", name2 = "acre-feet", symbol = "acre⋅ft", utype = "volume", scale = 1233.48183754752, default = "m3", }, ["AUtbsp"] = { name1 = "Australian tablespoon", symbol = "AU&nbsp;tbsp", utype = "volume", scale = 0.000020, default = "ml", }, ["Bcuft"] = { name1 = "billion cubic foot", name2 = "billion cubic feet", symbol = "billion cu&nbsp;ft", utype = "volume", scale = 28316846.592, default = "Gl", link = "Cubic foot", }, ["bdft"] = { name1 = "board foot", name2 = "board feet", symbol = "bd&nbsp;ft", utype = "volume", scale = 0.0023597372167, default = "m3", }, ["board feet"] = { name2 = "board feet", symbol = "board foot", usename = 1, utype = "volume", scale = 0.0023597372167, default = "m3", }, ["board foot"] = { name2 = "board foot", symbol = "board foot", usename = 1, utype = "volume", scale = 0.0023597372167, default = "m3", }, ["cc"] = { name1 = "cubic centimetre", name1_us = "cubic centimeter", symbol = "cc", utype = "volume", scale = 0.000001, default = "cuin", }, ["CID"] = { name1 = "cubic inch", name2 = "cubic inches", symbol = "cu&nbsp;in", utype = "volume", scale = 0.000016387064, default = "cc", link = "Cubic inch#Engine displacement", }, ["cord"] = { symbol = "cord", utype = "volume", scale = 3.624556363776, default = "m3", link = "Cord (unit)", }, ["cufoot"] = { name1 = "cubic foot", name2 = "cubic foot", symbol = "cu&nbsp;ft", utype = "volume", scale = 0.028316846592, default = "m3", }, ["cuft"] = { name1 = "cubic foot", name2 = "cubic feet", symbol = "cu&nbsp;ft", utype = "volume", scale = 0.028316846592, default = "m3", }, ["cuin"] = { name1 = "cubic inch", name2 = "cubic inches", symbol = "cu&nbsp;in", utype = "volume", scale = 0.000016387064, default = "cm3", }, ["cumi"] = { name1 = "cubic mile", symbol = "cu&nbsp;mi", utype = "volume", scale = 4168181825.440579584, default = "km3", }, ["cuyd"] = { name1 = "cubic yard", symbol = "cu&nbsp;yd", utype = "volume", scale = 0.764554857984, default = "m3", }, ["firkin"] = { symbol = "firkin", usename = 1, utype = "volume", scale = 0.04091481, default = "L impgal USgal", link = "Firkin (unit)", }, ["foot3"] = { target = "cufoot", }, ["Goilbbl"] = { name1 = "billion barrels", name2 = "billion barrels", symbol = "Gbbl", utype = "volume", scale = 158987294.928, default = "v * 1.58987294928 < 10 ! e6 ! e9 ! m3", link = "Barrel (unit)#Oil barrel", }, ["gr water"] = { name1 = "grains water", name2 = "grains water", symbol = "gr H<sub>2</sub>O", utype = "volume", scale = 0.00000006479891, default = "cm3", link = "Grain (unit)", }, ["grt"] = { name1 = "gross register ton", symbol = "grt", utype = "volume", scale = 2.8316846592, default = "m3", link = "Gross register tonnage", }, ["impbbl"] = { name1 = "imperial barrel", symbol = "imp&nbsp;bbl", utype = "volume", scale = 0.16365924, default = "L impgal USgal", link = "Barrel (unit)", }, ["impbsh"] = { name1 = "imperial bushel", symbol = "imp&nbsp;bsh", utype = "volume", scale = 0.03636872, default = "L impgal USdrygal", }, ["impbu"] = { name1 = "imperial bushel", symbol = "imp&nbsp;bu", utype = "volume", scale = 0.03636872, default = "m3", }, ["impgal"] = { name1 = "imperial gallon", symbol = "imp&nbsp;gal", utype = "volume", scale = 0.00454609, default = "L USgal", }, ["impgi"] = { name1 = "gill", symbol = "gi", utype = "volume", scale = 0.0001420653125, default = "ml USoz", link = "Gill (unit)", }, ["impkenning"] = { name1 = "imperial kenning", symbol = "kenning", utype = "volume", scale = 0.01818436, default = "L USdrygal", link = "Kenning (unit)", }, ["impoz"] = { name1 = "imperial fluid ounce", symbol = "imp&nbsp;fl&nbsp;oz", utype = "volume", scale = 0.0000284130625, default = "ml USoz", }, ["imppk"] = { name1 = "imperial peck", symbol = "pk", utype = "volume", scale = 0.00909218, default = "L USdrygal", link = "Peck", }, ["imppt"] = { name1 = "imperial pint", symbol = "imp&nbsp;pt", utype = "volume", scale = 0.00056826125, default = "L", }, ["impqt"] = { name1 = "imperial quart", symbol = "imp&nbsp;qt", utype = "volume", scale = 0.0011365225, default = "ml USoz", customary= 3, }, ["kilderkin"] = { symbol = "kilderkin", usename = 1, utype = "volume", scale = 0.08182962, default = "L impgal USgal", }, ["koilbbl"] = { name1 = "thousand barrels", name2 = "thousand barrels", symbol = "kbbl", utype = "volume", scale = 158.987294928, default = "v * 1.58987294928 < 10 ! ! e3 ! m3", link = "Barrel (unit)#Oil barrel", }, ["L"] = { _name1 = "litre", _name1_us= "liter", _symbol = "L", utype = "volume", scale = 0.001, prefixes = 1, default = "impgal USgal", link = "Litre", }, ["l"] = { _name1 = "litre", _name1_us= "liter", _symbol = "l", utype = "volume", scale = 0.001, prefixes = 1, default = "impgal USgal", link = "Litre", }, ["ll"] = { name1 = "litre", name1_us = "liter", symbol = "l", utype = "volume", scale = 0.001, default = "impgal USgal", }, ["m3"] = { _name1 = "cubic metre", _name1_us= "cubic meter", _symbol = "m<sup>3</sup>", prefix_position= 7, utype = "volume", scale = 1, prefixes = 3, default = "cuft", link = "Cubic metre", }, ["Mbbl"] = { name1 = "thousand barrels", name2 = "thousand barrels", symbol = "Mbbl", utype = "volume", scale = 158.987294928, default = "v * 1.58987294928 < 10 ! e3 ! ! m3", link = "Barrel (unit)#Oil barrel", }, ["MMoilbbl"] = { name1 = "million barrels", name2 = "million barrels", symbol = "MMbbl", utype = "volume", scale = 158987.294928, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3", link = "Barrel (unit)#Oil barrel", }, ["Moilbbl"] = { name1 = "million barrels", name2 = "million barrels", symbol = "Mbbl", utype = "volume", scale = 158987.294928, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3", link = "Barrel (unit)#Oil barrel", }, ["MTON"] = { name1 = "measurement ton", symbol = "MTON", utype = "volume", scale = 1.13267386368, default = "m3", }, ["MUSgal"] = { name1 = "million US gallons", name1_us = "million U.S. gallons", name2 = "million US gallons", name2_us = "million U.S. gallons", symbol = "million US&nbsp;gal", sym_us = "million U.S.&nbsp;gal", utype = "volume", scale = 3785.411784, default = "Ml", link = "US gallon", }, ["oilbbl"] = { name1 = "barrel", symbol = "bbl", utype = "volume", scale = 0.158987294928, default = "m3", link = "Barrel (unit)#Oil barrel", }, ["stere"] = { symbol = "stere", usename = 1, utype = "volume", scale = 1, default = "cuft", }, ["Toilbbl"] = { name1 = "trillion barrels", name2 = "trillion barrels", symbol = "Tbbl", utype = "volume", scale = 158987294928, default = "v * 1.58987294928 < 10 ! e9 ! e12 ! m3", link = "Barrel (unit)#Oil barrel", }, ["USbbl"] = { name1 = "US barrel", name1_us = "U.S. barrel", symbol = "US&nbsp;bbl", sym_us = "U.S.&nbsp;bbl", utype = "volume", scale = 0.119240471196, default = "L USgal impgal", link = "Barrel (unit)", }, ["USbeerbbl"] = { name1 = "US beer barrel", name1_us = "U.S. beer barrel", symbol = "US&nbsp;bbl", sym_us = "U.S.&nbsp;bbl", utype = "volume", scale = 0.117347765304, default = "L USgal impgal", link = "Barrel (unit)", }, ["USbsh"] = { name1 = "US bushel", name1_us = "U.S. bushel", symbol = "US&nbsp;bsh", sym_us = "U.S.&nbsp;bsh", utype = "volume", scale = 0.03523907016688, default = "L USdrygal impgal", link = "Bushel", }, ["USbu"] = { name1 = "US bushel", name1_us = "U.S. bushel", symbol = "US&nbsp;bu", sym_us = "U.S.&nbsp;bu", utype = "volume", scale = 0.03523907016688, default = "L USdrygal impgal", link = "Bushel", }, ["USdrybbl"] = { name1 = "US dry barrel", name1_us = "U.S. dry barrel", symbol = "US&nbsp;dry&nbsp;bbl", sym_us = "U.S.&nbsp;dry&nbsp;bbl", utype = "volume", scale = 0.11562819898508, default = "m3", link = "Barrel (unit)", }, ["USdrygal"] = { name1 = "US dry gallon", name1_us = "U.S. dry gallon", symbol = "US&nbsp;dry&nbsp;gal", sym_us = "U.S.&nbsp;dry&nbsp;gal", utype = "volume", scale = 0.00440488377086, default = "L", link = "Gallon", }, ["USdrypt"] = { name1 = "US dry pint", name1_us = "U.S. dry pint", symbol = "US&nbsp;dry&nbsp;pt", sym_us = "U.S.&nbsp;dry&nbsp;pt", utype = "volume", scale = 0.0005506104713575, default = "ml", link = "Pint", }, ["USdryqt"] = { name1 = "US dry quart", name1_us = "U.S. dry quart", symbol = "US&nbsp;dry&nbsp;qt", sym_us = "U.S.&nbsp;dry&nbsp;qt", utype = "volume", scale = 0.001101220942715, default = "ml", link = "Quart", }, ["USflgal"] = { name1 = "US gallon", name1_us = "U.S. gallon", symbol = "US fl gal", sym_us = "U.S.&nbsp;fl&nbsp;gal", utype = "volume", scale = 0.003785411784, default = "L impgal", link = "Gallon", }, ["USgal"] = { name1 = "US gallon", name1_us = "U.S. gallon", symbol = "US&nbsp;gal", sym_us = "U.S.&nbsp;gal", utype = "volume", scale = 0.003785411784, default = "L impgal", }, ["USgi"] = { name1 = "gill", symbol = "gi", utype = "volume", scale = 0.0001182941183, default = "ml impoz", link = "Gill (unit)", }, ["USkenning"] = { name1 = "US kenning", name1_us = "U.S. kenning", symbol = "US&nbsp;kenning", sym_us = "U.S.&nbsp;kenning", utype = "volume", scale = 0.01761953508344, default = "L impgal", link = "Kenning (unit)", }, ["USmin"] = { name1 = "US minim", name1_us = "U.S. minim", symbol = "US&nbsp;min", sym_us = "U.S.&nbsp;min", utype = "volume", scale = 0.000000061611519921875, default = "ml", link = "Minim (unit)", }, ["USoz"] = { name1 = "US fluid ounce", name1_us = "U.S. fluid ounce", symbol = "US&nbsp;fl&nbsp;oz", sym_us = "U.S.&nbsp;fl&nbsp;oz", utype = "volume", scale = 0.0000295735295625, default = "ml", }, ["USpk"] = { name1 = "US peck", name1_us = "U.S. peck", symbol = "US&nbsp;pk", sym_us = "U.S.&nbsp;pk", utype = "volume", scale = 0.00880976754172, default = "L impgal", link = "Peck", }, ["USpt"] = { name1 = "US pint", name1_us = "U.S. pint", symbol = "US&nbsp;pt", sym_us = "U.S.&nbsp;pt", utype = "volume", scale = 0.000473176473, default = "L imppt", link = "Pint", }, ["USqt"] = { name1 = "US quart", name1_us = "U.S. quart", symbol = "US&nbsp;qt", sym_us = "U.S.&nbsp;qt", utype = "volume", scale = 0.000946352946, default = "ml", link = "Quart", customary= 1, }, ["USquart"] = { name1 = "US quart", name1_us = "U.S. quart", symbol = "US&nbsp;qt", sym_us = "U.S.&nbsp;qt", utype = "volume", scale = 0.000946352946, default = "ml impoz", link = "Quart", }, ["UStbsp"] = { name1 = "US tablespoon", name1_us = "U.S. tablespoon", symbol = "US&nbsp;tbsp", sym_us = "U.S.&nbsp;tbsp", utype = "volume", scale = 1.4786764781250001e-5, default = "ml", }, ["winecase"] = { symbol = "case", usename = 1, utype = "volume", scale = 0.009, default = "L", link = "Case (goods)", }, ["*U.S.drygal"] = { target = "USdrygal", sp_us = true, customary= 2, }, ["*U.S.gal"] = { target = "USgal", sp_us = true, customary= 2, }, ["+USdrygal"] = { target = "USdrygal", customary= 1, }, ["+usfloz"] = { target = "USoz", link = "Fluid ounce", customary= 1, }, ["+USgal"] = { target = "USgal", customary= 1, }, ["+USoz"] = { target = "USoz", customary= 1, }, ["@impgal"] = { target = "impgal", link = "Gallon", customary= 3, }, ["acre feet"] = { target = "acre-ft", }, ["acre foot"] = { target = "acre-foot", }, ["acre ft"] = { target = "acre-ft", }, ["acre-feet"] = { target = "acre-ft", }, ["acre.foot"] = { target = "acre-foot", }, ["acre.ft"] = { target = "acre-ft", }, ["acre·ft"] = { target = "acre-ft", }, ["bushels"] = { target = "USbsh", }, ["cid"] = { target = "CID", }, ["ft3"] = { target = "cuft", }, ["gal"] = { target = "USgal", }, ["gallon"] = { shouldbe = "Use %{USgal%} for US gallons or %{impgal%} for imperial gallons (not %{gallon%})", }, ["gallons"] = { shouldbe = "Use %{USgal%} for US gallons or %{impgal%} for imperial gallons (not %{gallons%})", }, ["Gcuft"] = { target = "e9cuft", }, ["impfloz"] = { target = "impoz", }, ["Impgal"] = { target = "impgal", }, ["in3"] = { target = "cuin", symbol = "in<sup>3</sup>", }, ["hm³"] = { target = "hm3", }, ["kcuft"] = { target = "e3cuft", }, ["kcum"] = { target = "e3m3", }, ["km³"] = { target = "km3", }, ["liter"] = { target = "L", sp_us = true, }, ["liters"] = { target = "L", sp_us = true, }, ["litre"] = { target = "L", }, ["litres"] = { target = "L", }, ["Mcuft"] = { target = "e6cuft", }, ["Mcum"] = { target = "e6m3", }, ["Mft3"] = { target = "e6cuft", }, ["mi3"] = { target = "cumi", }, ["m³"] = { target = "m3", }, ["Pcuft"] = { target = "e15cuft", }, ["pt"] = { shouldbe = "Use %{USpt%} for US pints or %{imppt%} for imperial pints (not %{pt%})", }, ["qt"] = { shouldbe = "Use %{USqt%} for US quarts or %{impqt%} for imperial quarts (not %{qt%})", }, ["Tcuft"] = { target = "e12cuft", }, ["Tft3"] = { target = "e12cuft", }, ["U.S.bbl"] = { target = "USbbl", sp_us = true, default = "L U.S.gal impgal", }, ["U.S.beerbbl"] = { target = "USbeerbbl", sp_us = true, default = "L U.S.gal impgal", }, ["U.S.bsh"] = { target = "USbsh", sp_us = true, default = "L U.S.drygal impgal", }, ["U.S.bu"] = { target = "USbu", sp_us = true, default = "L U.S.drygal impgal", }, ["U.S.drybbl"] = { target = "USdrybbl", sp_us = true, }, ["U.S.drygal"] = { target = "USdrygal", sp_us = true, }, ["U.S.drypt"] = { target = "USdrypt", sp_us = true, }, ["U.S.dryqt"] = { target = "USdryqt", sp_us = true, }, ["U.S.flgal"] = { target = "USflgal", sp_us = true, }, ["U.S.floz"] = { target = "USoz", sp_us = true, }, ["U.S.gal"] = { target = "USgal", sp_us = true, link = "U.S. gallon", }, ["u.s.gal"] = { target = "USgal", sp_us = true, link = "U.S. gallon", }, ["U.S.gi"] = { target = "USgi", sp_us = true, }, ["U.S.kenning"] = { target = "USkenning", sp_us = true, }, ["U.S.oz"] = { target = "USoz", sp_us = true, }, ["U.S.pk"] = { target = "USpk", sp_us = true, }, ["U.S.pt"] = { target = "USpt", sp_us = true, }, ["U.S.qt"] = { target = "USqt", sp_us = true, default = "L impqt", customary= 2, }, ["usbbl"] = { target = "USbbl", }, ["usbeerbbl"] = { target = "USbeerbbl", }, ["usbsh"] = { target = "USbsh", }, ["usbu"] = { target = "USbu", }, ["usdrybbl"] = { target = "USdrybbl", }, ["usdrygal"] = { target = "USdrygal", }, ["usdrypt"] = { target = "USdrypt", }, ["usdryqt"] = { target = "USdryqt", }, ["USfloz"] = { target = "USoz", }, ["usfloz"] = { target = "USoz", }, ["USGAL"] = { target = "USgal", }, ["usgal"] = { target = "USgal", }, ["usgi"] = { target = "USgi", }, ["uskenning"] = { target = "USkenning", }, ["usoz"] = { target = "USoz", }, ["uspk"] = { target = "USpk", }, ["uspt"] = { target = "USpt", }, ["usqt"] = { target = "USqt", }, ["yd3"] = { target = "cuyd", }, ["cuft/sqmi"] = { per = { "cuft", "sqmi" }, utype = "volume per unit area", default = "m3/km2", }, ["m3/ha"] = { name1 = "cubic metre per hectare", name1_us = "cubic meter per hectare", name2 = "cubic metres per hectare", name2_us = "cubic meters per hectare", symbol = "m<sup>3</sup>/ha", utype = "volume per unit area", scale = 0.0001, default = "USbu/acre", link = "Hectare", }, ["m3/km2"] = { per = { "m3", "km2" }, utype = "volume per unit area", default = "cuft/sqmi", }, ["U.S.gal/acre"] = { per = { "U.S.gal", "acre" }, utype = "volume per unit area", default = "m3/km2", }, ["USbu/acre"] = { name2 = "US bushels per acre", symbol = "US bushel per acre", usename = 1, utype = "volume per unit area", scale = 8.7077638761350888e-6, default = "m3/ha", link = "Bushel", }, ["USgal/acre"] = { per = { "USgal", "acre" }, utype = "volume per unit area", default = "m3/km2", }, ["cuyd/mi"] = { per = { "cuyd", "mi" }, utype = "volume per unit length", default = "m3/km", }, ["m3/km"] = { per = { "m3", "km" }, utype = "volume per unit length", default = "cuyd/mi", }, ["mich"] = { combination= { "ch", "mi" }, multiple = { 80 }, utype = "length", }, ["michlk"] = { combination= { "chlk", "mi" }, multiple = { 80 }, utype = "length", }, ["michainlk"] = { combination= { "chainlk", "mi" }, multiple = { 80 }, utype = "length", }, ["miyd"] = { combination= { "yd", "mi" }, multiple = { 1760 }, utype = "length", }, ["miydftin"] = { combination= { "in", "ft", "yd", "mi" }, multiple = { 12, 3, 1760 }, utype = "length", }, ["mift"] = { combination= { "ft", "mi" }, multiple = { 5280 }, utype = "length", }, ["ydftin"] = { combination= { "in", "ft", "yd" }, multiple = { 12, 3 }, utype = "length", }, ["ydft"] = { combination= { "ft", "yd" }, multiple = { 3 }, utype = "length", }, ["ftin"] = { combination= { "in", "ft" }, multiple = { 12 }, utype = "length", }, ["footin"] = { combination= { "in", "foot" }, multiple = { 12 }, utype = "length", }, ["handin"] = { combination= { "in", "hand" }, multiple = { 4 }, utype = "length", }, ["lboz"] = { combination= { "oz", "lb" }, multiple = { 16 }, utype = "mass", }, ["stlb"] = { combination= { "lb", "st" }, multiple = { 14 }, utype = "mass", }, ["stlboz"] = { combination= { "oz", "lb", "st" }, multiple = { 16, 14 }, utype = "mass", }, ["st and lb"] = { combination= { "lb", "st" }, multiple = { 14 }, utype = "mass", }, ["GN LTf"] = { combination= { "GN", "-LTf" }, utype = "force", }, ["GN LTf STf"] = { combination= { "GN", "-LTf", "-STf" }, utype = "force", }, ["GN STf"] = { combination= { "GN", "-STf" }, utype = "force", }, ["GN STf LTf"] = { combination= { "GN", "-STf", "-LTf" }, utype = "force", }, ["kN LTf"] = { combination= { "kN", "-LTf" }, utype = "force", }, ["kN LTf STf"] = { combination= { "kN", "-LTf", "-STf" }, utype = "force", }, ["kN STf"] = { combination= { "kN", "-STf" }, utype = "force", }, ["kN STf LTf"] = { combination= { "kN", "-STf", "-LTf" }, utype = "force", }, ["LTf STf"] = { combination= { "-LTf", "-STf" }, utype = "force", }, ["MN LTf"] = { combination= { "MN", "-LTf" }, utype = "force", }, ["MN LTf STf"] = { combination= { "MN", "-LTf", "-STf" }, utype = "force", }, ["MN STf"] = { combination= { "MN", "-STf" }, utype = "force", }, ["MN STf LTf"] = { combination= { "MN", "-STf", "-LTf" }, utype = "force", }, ["STf LTf"] = { combination= { "-STf", "-LTf" }, utype = "force", }, ["L/100 km mpgimp"] = { combination= { "L/100 km", "mpgimp" }, utype = "fuel efficiency", }, ["l/100 km mpgimp"] = { combination= { "l/100 km", "mpgimp" }, utype = "fuel efficiency", }, ["L/100 km mpgUS"] = { combination= { "L/100 km", "mpgus" }, utype = "fuel efficiency", }, ["L/100 km mpgus"] = { combination= { "L/100 km", "mpgus" }, utype = "fuel efficiency", }, ["l/100 km mpgus"] = { combination= { "l/100 km", "mpgus" }, utype = "fuel efficiency", }, ["mpgimp L/100 km"] = { combination= { "mpgimp", "L/100 km" }, utype = "fuel efficiency", }, ["LT ST t"] = { combination= { "lt", "-ST", "t" }, utype = "mass", }, ["LT t ST"] = { combination= { "lt", "t", "-ST" }, utype = "mass", }, ["ST LT t"] = { combination= { "-ST", "lt", "t" }, utype = "mass", }, ["ST t LT"] = { combination= { "-ST", "t", "lt" }, utype = "mass", }, ["t LT ST"] = { combination= { "t", "lt", "-ST" }, utype = "mass", }, ["ton"] = { combination= { "LT", "ST" }, utype = "mass", }, ["kPa kg/cm2"] = { combination= { "kPa", "kgf/cm2" }, utype = "pressure", }, ["kPa lb/in2"] = { combination= { "kPa", "-lb/in2" }, utype = "pressure", }, ["floz"] = { combination= { "impoz", "USoz" }, utype = "volume", }, } --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local default_exceptions = { -- Prefixed units with a default different from that of the base unit. -- Each key item is a prefixed symbol (unitcode for engineering notation). ["cm<sup>2</sup>"] = "sqin", ["dm<sup>2</sup>"] = "sqin", ["e3acre"] = "km2", ["e3m2"] = "e6sqft", ["e6acre"] = "km2", ["e6ha"] = "e6acre", ["e6km2"] = "e6sqmi", ["e6m2"] = "e6sqft", ["e6sqft"] = "v * 9.290304 < 100 ! e3 ! e6 ! m2", ["e6sqmi"] = "e6km2", ["hm<sup>2</sup>"] = "acre", ["km<sup>2</sup>"] = "sqmi", ["mm<sup>2</sup>"] = "sqin", ["aJ"] = "eV", ["e3BTU"] = "MJ", ["e6BTU"] = "GJ", ["EJ"] = "kWh", ["fJ"] = "keV", ["GJ"] = "kWh", ["MJ"] = "kWh", ["PJ"] = "kWh", ["pJ"] = "MeV", ["TJ"] = "kWh", ["YJ"] = "kWh", ["yJ"] = "μeV", ["ZJ"] = "kWh", ["zJ"] = "meV", ["e12cuft/a"] = "v * 2.8316846592 < 100 ! e9 ! e12 ! m3/a", ["e12cuft/d"] = "v * 2.8316846592 < 100 ! e9 ! e12 ! m3/d", ["e12m3/a"] = "Tcuft/a", ["e12m3/d"] = "Tcuft/d", ["e3cuft/a"] = "v * 2.8316846592 < 100 ! ! e3 ! m3/a", ["e3cuft/d"] = "v * 2.8316846592 < 100 ! ! e3 ! m3/d", ["e3cuft/s"] = "v * 2.8316846592 < 100 ! ! e3 ! m3/s", ["e3m3/a"] = "v < 28.316846592 ! k ! M ! cuft/a", ["e3m3/d"] = "v < 28.316846592 ! k ! M ! cuft/d", ["e3m3/s"] = "v < 28.316846592 ! k ! M ! cuft/s", ["e3USgal/a"] = "v * 3.785411784 < 1000 ! ! e3 ! m3/a", ["e6cuft/a"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3/a", ["e6cuft/d"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3/d", ["e6cuft/s"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3/s", ["e6m3/a"] = "v < 28.316846592 ! M ! G ! cuft/a", ["e6m3/d"] = "v < 28.316846592 ! M ! G ! cuft/d", ["e6m3/s"] = "v < 28.316846592 ! e6 ! e9 ! cuft/s", ["e6USgal/a"] = "v * 3.785411784 < 1000 ! e3 ! e6 ! m3/a", ["e9cuft/a"] = "m3/a", ["e9cuft/d"] = "v * 2.8316846592 < 100 ! e6 ! e9 ! m3/d", ["e9m3/a"] = "v < 28.316846592 ! G ! T ! cuft/a", ["e9m3/d"] = "v < 28.316846592 ! G ! T ! cuft/d", ["e9m3/s"] = "v < 28.316846592 ! e9 ! e12 ! cuft/s", ["e9USgal/a"] = "v * 3.785411784 < 1000 ! e6 ! e9 ! m3/a", ["e9USgal/s"] = "v * 3.785411784 < 1000 ! e6 ! e9 ! m3/s", ["nN"] = "gr-f", ["μN"] = "gr-f", ["mN"] = "oz-f", ["am"] = "in", ["cm"] = "in", ["dam"] = "ft", ["dm"] = "in", ["e12km"] = "e12mi", ["e12mi"] = "e12km", ["e3AU"] = "ly", ["e3km"] = "e3mi", ["e3mi"] = "e3km", ["e6km"] = "e6mi", ["e6mi"] = "e6km", ["e9km"] = "AU", ["e9mi"] = "e9km", ["Em"] = "mi", ["fm"] = "in", ["Gm"] = "mi", ["hm"] = "ft", ["km"] = "mi", ["mm"] = "in", ["Mm"] = "mi", ["nm"] = "in", ["Pm"] = "mi", ["pm"] = "in", ["Tm"] = "mi", ["Ym"] = "mi", ["ym"] = "in", ["Zm"] = "mi", ["zm"] = "in", ["μm"] = "in", ["e12lb"] = "v * 4.5359237 < 10 ! Mt ! Gt", ["e3lb"] = "v * 4.5359237 < 10 ! kg ! t", ["e3ozt"] = "v * 0.311034768 < 10 ! kg ! t", ["e3t"] = "LT ST", ["e6carat"] = "t", ["e6lb"] = "v * 4.5359237 < 10 ! t ! kilotonne", ["e6ozt"] = "lb kg", ["e6ST"] = "Mt", ["e6t"] = "LT ST", ["e9lb"] = "v * 4.5359237 < 10 ! kilotonne ! Mt", ["e9t"] = "LT ST", ["Gg"] = "lb", ["kg"] = "lb", ["mg"] = "gr", ["Mg"] = "LT ST", ["ng"] = "gr", ["μg"] = "gr", ["mBq"] = "fCi", ["kBq"] = "nCi", ["MBq"] = "μCi", ["GBq"] = "mCi", ["TBq"] = "Ci", ["PBq"] = "kCi", ["EBq"] = "kCi", ["fCi"] = "mBq", ["pCi"] = "Bq", ["nCi"] = "Bq", ["μCi"] = "kBq", ["mCi"] = "MBq", ["kCi"] = "TBq", ["MCi"] = "PBq", ["ns"] = "μs", ["μs"] = "ms", ["ms"] = "s", ["ks"] = "h", ["Ms"] = "week", ["Gs"] = "decade", ["Ts"] = "millennium", ["Ps"] = "million year", ["Es"] = "thousand million year", ["MK"] = "keVT", ["cL"] = "impoz usoz", ["cl"] = "impoz usoz", ["cm<sup>3</sup>"] = "cuin", ["dL"] = "impoz usoz", ["dl"] = "impoz usoz", ["mm<sup>3</sup>"] = "cuin", ["dm<sup>3</sup>"] = "cuin", ["e12cuft"] = "v * 2.8316846592 < 100 ! e9 ! e12 ! m3", ["e12impgal"] = "v * 4.54609 < 1000 ! T ! P ! l", ["e12m3"] = "v < 28.316846592 ! T ! P ! cuft", ["e12U.S.gal"] = "v * 3.785411784 < 1000 ! T ! P ! l", ["e12USgal"] = "v * 3.785411784 < 1000 ! T ! P ! l", ["e15cuft"] = "v * 2.8316846592 < 100 ! e12 ! e15 ! m3", ["e15m3"] = "Pcuft", ["e3bdft"] = "v * 0.23597372167 < 100 ! e3 ! e6 ! m3", ["e3cuft"] = "v * 2.8316846592 < 100 ! ! e3 ! m3", ["e3impgal"] = "v * 4.54609 < 1000 ! k ! M ! l", ["e3m3"] = "v < 28.316846592 ! k ! M ! cuft", ["e3U.S.gal"] = "v * 3.785411784 < 1000 ! k ! M ! l", ["e3USgal"] = "v * 3.785411784 < 1000 ! k ! M ! l", ["e6bdft"] = "v * 0.23597372167 < 100 ! e3 ! e6 ! m3", ["e6cuft"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3", ["e6cuyd"] = "v * 7.64554857984 < 10 ! e3 ! e6 ! m3", ["e6impgal"] = "v * 4.54609 < 1000 ! M ! G ! l", ["e6L"] = "USgal", ["e6m3"] = "v < 28.316846592 ! M ! G ! cuft", ["e6U.S.gal"] = "v * 3.785411784 < 1000 ! M ! G ! l", ["e6USgal"] = "v * 3.785411784 < 1000 ! M ! G ! l", ["e9bdft"] = "v * 0.23597372167 < 100 ! e6 ! e9 ! m3", ["e9cuft"] = "v * 2.8316846592 < 100 ! e6 ! e9 ! m3", ["e9impgal"] = "v * 4.54609 < 1000 ! G ! T ! l", ["e9m3"] = "v < 28.316846592 ! G ! T ! cuft", ["e9U.S.gal"] = "v * 3.785411784 < 1000 ! G ! T ! l", ["e9USgal"] = "v * 3.785411784 < 1000 ! G ! T ! l", ["GL"] = "cuft", ["Gl"] = "cuft", ["kL"] = "cuft", ["kl"] = "cuft", ["km<sup>3</sup>"] = "cumi", ["mL"] = "impoz usoz", ["ml"] = "impoz usoz", ["Ml"] = "v < 28.316846592 ! e3 ! e6 ! cuft", ["ML"] = "v < 28.316846592 ! e3 ! e6 ! cuft", ["TL"] = "cumi", ["Tl"] = "cumi", ["μL"] = "cuin", ["μl"] = "cuin", } --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local link_exceptions = { -- Prefixed units with a linked article different from that of the base unit. -- Each key item is a prefixed symbol (not unitcode). ["mm<sup>2</sup>"] = "Square millimetre", ["cm<sup>2</sup>"] = "Square centimetre", ["dm<sup>2</sup>"] = "Square decimetre", ["km<sup>2</sup>"] = "Square kilometre", ["kJ"] = "Kilojoule", ["MJ"] = "Megajoule", ["GJ"] = "Gigajoule", ["TJ"] = "Terajoule", ["fm"] = "Femtometre", ["pm"] = "Picometre", ["nm"] = "Nanometre", ["μm"] = "Micrometre", ["mm"] = "Millimetre", ["cm"] = "Centimetre", ["dm"] = "Decimetre", ["dam"] = "Decametre", ["hm"] = "Hectometre", ["km"] = "Kilometre", ["Mm"] = "Megametre", ["Gm"] = "Gigametre", ["Tm"] = "Terametre", ["Pm"] = "Petametre", ["Em"] = "Exametre", ["Zm"] = "Zettametre", ["Ym"] = "Yottametre", ["μg"] = "Microgram", ["mg"] = "Milligram", ["kg"] = "Kilogram", ["Mg"] = "Tonne", ["yW"] = "Yoctowatt", ["zW"] = "Zeptowatt", ["aW"] = "Attowatt", ["fW"] = "Femtowatt", ["pW"] = "Picowatt", ["nW"] = "Nanowatt", ["μW"] = "Microwatt", ["mW"] = "Milliwatt", ["kW"] = "Kilowatt", ["MW"] = "Megawatt", ["GW"] = "Gigawatt", ["TW"] = "Terawatt", ["PW"] = "Petawatt", ["EW"] = "Exawatt", ["ZW"] = "Zettawatt", ["YW"] = "Yottawatt", ["as"] = "Attosecond", ["fs"] = "Femtosecond", ["ps"] = "Picosecond", ["ns"] = "Nanosecond", ["μs"] = "Microsecond", ["ms"] = "Millisecond", ["ks"] = "Kilosecond", ["Ms"] = "Megasecond", ["Gs"] = "Gigasecond", ["Ts"] = "Terasecond", ["Ps"] = "Petasecond", ["Es"] = "Exasecond", ["Zs"] = "Zettasecond", ["Ys"] = "Yottasecond", ["mm<sup>3</sup>"] = "Cubic millimetre", ["cm<sup>3</sup>"] = "Cubic centimetre", ["dm<sup>3</sup>"] = "Cubic decimetre", ["dam<sup>3</sup>"] = "Cubic decametre", ["km<sup>3</sup>"] = "Cubic kilometre", ["μL"] = "Microlitre", ["μl"] = "Microlitre", ["mL"] = "Millilitre", ["ml"] = "Millilitre", ["cL"] = "Centilitre", ["cl"] = "Centilitre", ["dL"] = "Decilitre", ["dl"] = "Decilitre", ["daL"] = "Decalitre", ["dal"] = "Decalitre", ["hL"] = "Hectolitre", ["hl"] = "Hectolitre", ["kL"] = "Kilolitre", ["kl"] = "Kilolitre", ["ML"] = "Megalitre", ["Ml"] = "Megalitre", ["GL"] = "Gigalitre", ["Gl"] = "Gigalitre", ["TL"] = "Teralitre", ["Tl"] = "Teralitre", ["PL"] = "Petalitre", ["Pl"] = "Petalitre", } --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local per_unit_fixups = { -- Automatically created per units of form "x/y" may have their unit type -- changed, for example, "length/time" is changed to "speed". -- Other adjustments can also be specified. ["/area"] = "per unit area", ["/volume"] = "per unit volume", ["area/area"] = "area per unit area", ["energy/length"] = "energy per unit length", ["energy/mass"] = "energy per unit mass", ["energy/time"] = { utype = "power", link = "Power (physics)" }, ["energy/volume"] = "energy per unit volume", ["force/area"] = { utype = "pressure", link = "Pressure" }, ["length/length"] = { utype = "gradient", link = "Grade (slope)" }, ["length/time"] = { utype = "speed", link = "Speed" }, ["length/time/time"] = { utype = "acceleration", link = "Acceleration" }, ["mass/area"] = { utype = "pressure", multiplier = 9.80665 }, ["mass/length"] = "linear density", ["mass/mass"] = "concentration", ["mass/power"] = "mass per unit power", ["mass/time"] = "mass per unit time", ["mass/volume"] = { utype = "density", link = "Density" }, ["power/mass"] = "power per unit mass", ["power/volume"] = { link = "Power density" }, ["pressure/length"] = "fracture gradient", ["speed/time"] = { utype = "acceleration", link = "Acceleration" }, ["volume/area"] = "volume per unit area", ["volume/length"] = "volume per unit length", ["volume/time"] = "flow", } return { all_units = all_units, default_exceptions = default_exceptions, link_exceptions = link_exceptions, per_unit_fixups = per_unit_fixups, } 8d4f7e3d03f55a7683bae4e0b800b77ac91d44a2 Module:Convert 828 496 992 2023-05-10T03:20:21Z string2>Johnuniq 0 update from sandbox per [[Template talk:Convert#Module version 29]] Scribunto text/plain -- Convert a value from one unit of measurement to another. -- Example: {{convert|123|lb|kg}} --> 123 pounds (56 kg) -- See [[:en:Template:Convert/Transwiki guide]] if copying to another wiki. local MINUS = '−' -- Unicode U+2212 MINUS SIGN (UTF-8: e2 88 92) local abs = math.abs local floor = math.floor local format = string.format local log10 = math.log10 local ustring = mw.ustring local ulen = ustring.len local usub = ustring.sub -- Configuration options to keep magic values in one location. -- Conversion data and message text are defined in separate modules. local config, maxsigfig local numdot -- must be '.' or ',' or a character which works in a regex local numsep, numsep_remove, numsep_remove2 local data_code, all_units local text_code local varname -- can be a code to use variable names that depend on value local from_en_table -- to translate an output string of en digits to local language local to_en_table -- to translate an input string of digits in local language to en -- Use translation_table in convert/text to change the following. local en_default -- true uses lang=en unless convert has lang=local or local digits local group_method = 3 -- code for how many digits are in a group local per_word = 'per' -- for units like "liters per kilometer" local plural_suffix = 's' -- only other useful value is probably '' to disable plural unit names local omitsep -- true to omit separator before local symbol/name -- All units should be defined in the data module. However, to cater for quick changes -- and experiments, any unknown unit is looked up in an extra data module, if it exists. -- That module would be transcluded in only a small number of pages, so there should be -- little server overhead from making changes, and changes should propagate quickly. local extra_module -- name of module with extra units local extra_units -- nil or table of extra units from extra_module -- Some options in the invoking template can set variables used later in the module. local currency_text -- for a user-defined currency symbol: {{convert|12|$/ha|$=€}} (euro replaces dollar) local function from_en(text) -- Input is a string representing a number in en digits with '.' decimal mark, -- without digit grouping (which is done just after calling this). -- Return the translation of the string with numdot and digits in local language. if numdot ~= '.' then text = text:gsub('%.', numdot) end if from_en_table then text = text:gsub('%d', from_en_table) end return text end local function to_en(text) -- Input is a string representing a number in the local language with -- an optional numdot decimal mark and numsep digit grouping. -- Return the translation of the string with '.' mark and en digits, -- and no separators (they have to be removed here to handle cases like -- numsep = '.' and numdot = ',' with input "1.234.567,8"). if to_en_table then text = ustring.gsub(text, '%d', to_en_table) end if numsep_remove then text = text:gsub(numsep_remove, '') end if numsep_remove2 then text = text:gsub(numsep_remove2, '') end if numdot ~= '.' then text = text:gsub(numdot, '.') end return text end local function decimal_mark(text) -- Return ',' if text probably is using comma for decimal mark, or has no decimal mark. -- Return '.' if text probably is using dot for decimal mark. -- Otherwise return nothing (decimal mark not known). if not text:find('[.,]') then return ',' end text = text:gsub('^%-', ''):gsub('%+%d+/%d+$', ''):gsub('[Ee]%-?%d+$', '') local decimal = text:match('^0?([.,])%d+$') or text:match('%d([.,])%d?%d?$') or text:match('%d([.,])%d%d%d%d+$') if decimal then return decimal end if text:match('%.%d+%.') then return ',' end if text:match('%,%d+,') then return '.' end end local add_warning, with_separator -- forward declarations local function to_en_with_check(text, parms) -- Version of to_en() for a wiki using numdot = ',' and numsep = '.' to check -- text (an input number as a string) which might have been copied from enwiki. -- For example, in '1.234' the '.' could be a decimal mark or a group separator. -- From viwiki. if to_en_table then text = ustring.gsub(text, '%d', to_en_table) end if decimal_mark(text) == '.' then local original = text text = text:gsub(',', '') -- for example, interpret "1,234.5" as an enwiki value if parms then add_warning(parms, 0, 'cvt_enwiki_num', original, with_separator({}, text)) end else if numsep_remove then text = text:gsub(numsep_remove, '') end if numsep_remove2 then text = text:gsub(numsep_remove2, '') end if numdot ~= '.' then text = text:gsub(numdot, '.') end end return text end local function omit_separator(id) -- Return true if there should be no separator before id (a unit symbol or name). -- For zhwiki, there should be no separator if id uses local characters. -- The following kludge should be a sufficient test. if omitsep then if id:sub(1, 2) == '-{' then -- for "-{...}-" content language variant return true end if id:byte() > 127 then local first = usub(id, 1, 1) if first ~= 'Å' and first ~= '°' and first ~= 'µ' then return true end end end return id:sub(1, 1) == '/' -- no separator before units like "/ha" end local spell_module -- name of module that can spell numbers local speller -- function from that module to handle spelling (set if needed) local wikidata_module, wikidata_data_module -- names of Wikidata modules local wikidata_code, wikidata_data -- exported tables from those modules (set if needed) local function set_config(args) -- Set configuration options from template #invoke or defaults. config = args maxsigfig = config.maxsigfig or 14 -- maximum number of significant figures local data_module, text_module local sandbox = config.sandbox and ('/' .. config.sandbox) or '' data_module = "Module:Convert/data" .. sandbox text_module = "Module:Convert/text" .. sandbox extra_module = "Module:Convert/extra" .. sandbox wikidata_module = "Module:Convert/wikidata" .. sandbox wikidata_data_module = "Module:Convert/wikidata/data" .. sandbox spell_module = "Module:ConvertNumeric" data_code = mw.loadData(data_module) text_code = mw.loadData(text_module) all_units = data_code.all_units local translation = text_code.translation_table if translation then numdot = translation.numdot numsep = translation.numsep if numdot == ',' and numsep == '.' then if text_code.all_messages.cvt_enwiki_num then to_en = to_en_with_check end end if translation.group then group_method = translation.group end if translation.per_word then per_word = translation.per_word end if translation.plural_suffix then plural_suffix = translation.plural_suffix end varname = translation.varname from_en_table = translation.from_en local use_workaround = true if use_workaround then -- 2013-07-05 workaround bug by making a copy of the required table. -- mw.ustring.gsub fails with a table (to_en_table) as the replacement, -- if the table is accessed via mw.loadData. local source = translation.to_en if source then to_en_table = {} for k, v in pairs(source) do to_en_table[k] = v end end else to_en_table = translation.to_en end if translation.lang == 'en default' then en_default = true -- for hiwiki end omitsep = translation.omitsep -- for zhwiki end numdot = config.numdot or numdot or '.' -- decimal mark before fractional digits numsep = config.numsep or numsep or ',' -- group separator for numbers -- numsep should be ',' or '.' or '' or '&nbsp;' or a Unicode character. -- numsep_remove must work in a regex to identify separators to be removed. if numsep ~= '' then numsep_remove = (numsep == '.') and '%.' or numsep end if numsep ~= ',' and numdot ~= ',' then numsep_remove2 = ',' -- so numbers copied from enwiki will work end end local function collection() -- Return a table to hold items. return { n = 0, add = function (self, item) self.n = self.n + 1 self[self.n] = item end, } end local function divide(numerator, denominator) -- Return integers quotient, remainder resulting from dividing the two -- given numbers, which should be unsigned integers. local quotient, remainder = floor(numerator / denominator), numerator % denominator if not (0 <= remainder and remainder < denominator) then -- Floating point limits may need this, as in {{convert|160.02|Ym|ydftin}}. remainder = 0 end return quotient, remainder end local function split(text, delimiter) -- Return a numbered table with fields from splitting text. -- The delimiter is used in a regex without escaping (for example, '.' would fail). -- Each field has any leading/trailing whitespace removed. local t = {} text = text .. delimiter -- to get last item for item in text:gmatch('%s*(.-)%s*' .. delimiter) do table.insert(t, item) end return t end local function strip(text) -- If text is a string, return its content with no leading/trailing -- whitespace. Otherwise return nil (a nil argument gives a nil result). if type(text) == 'string' then return text:match("^%s*(.-)%s*$") end end local function table_len(t) -- Return length (<100) of a numbered table to replace #t which is -- documented to not work if t is accessed via mw.loadData(). for i = 1, 100 do if t[i] == nil then return i - 1 end end end local function wanted_category(catkey, catsort, want_warning) -- Return message category if it is wanted in current namespace, -- otherwise return ''. local cat local title = mw.title.getCurrentTitle() if title then local nsdefault = '0' -- default namespace: '0' = article; '0,10' = article and template local namespace = title.namespace for _, v in ipairs(split(config.nscat or nsdefault, ',')) do if namespace == tonumber(v) then cat = text_code.all_categories[want_warning and 'warning' or catkey] if catsort and catsort ~= '' and cat:sub(-2) == ']]' then cat = cat:sub(1, -3) .. '|' .. mw.text.nowiki(usub(catsort, 1, 20)) .. ']]' end break end end end return cat or '' end local function message(parms, mcode, is_warning) -- Return wikitext for an error message, including category if specified -- for the message type. -- mcode = numbered table specifying the message: -- mcode[1] = 'cvt_xxx' (string used as a key to get message info) -- mcode[2] = 'parm1' (string to replace '$1' if any in message) -- mcode[3] = 'parm2' (string to replace '$2' if any in message) -- mcode[4] = 'parm3' (string to replace '$3' if any in message) local msg if type(mcode) == 'table' then if mcode[1] == 'cvt_no_output' then -- Some errors should cause convert to output an empty string, -- for example, for an optional field in an infobox. return '' end msg = text_code.all_messages[mcode[1]] end parms.have_problem = true local function subparm(fmt, ...) local rep = {} for i, v in ipairs({...}) do rep['$' .. i] = v end return (fmt:gsub('$%d+', rep)) end if msg then local parts = {} local regex, replace = msg.regex, msg.replace for i = 1, 3 do local limit = 40 local s = mcode[i + 1] if s then if regex and replace then s = s:gsub(regex, replace) limit = nil -- allow long "should be" messages end -- Escape user input so it does not break the message. -- To avoid tags (like {{convert|1<math>23</math>|m}}) breaking -- the mouseover title, any strip marker starting with char(127) is -- replaced with '...' (text not needing i18n). local append local pos = s:find(string.char(127), 1, true) if pos then append = '...' s = s:sub(1, pos - 1) end if limit and ulen(s) > limit then s = usub(s, 1, limit) append = '...' end s = mw.text.nowiki(s) .. (append or '') else s = '?' end parts['$' .. i] = s end local function ispreview() -- Return true if a prominent message should be shown. if parms.test == 'preview' or parms.test == 'nopreview' then -- For testing, can preview a real message or simulate a preview -- when running automated tests. return parms.test == 'preview' end local success, revid = pcall(function () return (parms.frame):preprocess('{{REVISIONID}}') end) return success and (revid == '') end local want_warning = is_warning and not config.warnings and -- show unobtrusive warnings if config.warnings not configured not msg.nowarn -- but use msg settings, not standard warning, if specified local title = string.gsub(msg[1] or 'Missing message', '$%d+', parts) local text = want_warning and '*' or msg[2] or 'Missing message' local cat = wanted_category(msg[3], mcode[2], want_warning) local anchor = msg[4] or '' local fmtkey = ispreview() and 'cvt_format_preview' or (want_warning and 'cvt_format2' or msg.format or 'cvt_format') local fmt = text_code.all_messages[fmtkey] or 'convert: bug' return subparm(fmt, title:gsub('"', '&quot;'), text, cat, anchor) end return 'Convert internal error: unknown message' end function add_warning(parms, level, key, text1, text2) -- for forward declaration above -- If enabled, add a warning that will be displayed after the convert result. -- A higher level is more verbose: more kinds of warnings are displayed. -- To reduce output noise, only the first warning is displayed. if level <= (tonumber(config.warnings) or 1) then if parms.warnings == nil then parms.warnings = message(parms, { key, text1, text2 }, true) end end end local function spell_number(parms, inout, number, numerator, denominator) -- Return result of spelling (number, numerator, denominator), or -- return nil if spelling is not available or not supported for given text. -- Examples (each value must be a string or nil): -- number numerator denominator output -- ------ --------- ----------- ------------------- -- "1.23" nil nil one point two three -- "1" "2" "3" one and two thirds -- nil "2" "3" two thirds if not speller then local function get_speller(module) return require(module).spell_number end local success success, speller = pcall(get_speller, spell_module) if not success or type(speller) ~= 'function' then add_warning(parms, 1, 'cvt_no_spell', 'spell') return nil end end local case if parms.spell_upper == inout then case = true parms.spell_upper = nil -- only uppercase first word in a multiple unit end local sp = not parms.opt_sp_us local adj = parms.opt_adjectival return speller(number, numerator, denominator, case, sp, adj) end ------------------------------------------------------------------------ -- BEGIN: Code required only for built-in units. -- LATER: If need much more code, move to another module to simplify this module. local function speed_of_sound(altitude) -- This is for the Mach built-in unit of speed. -- Return speed of sound in metres per second at given altitude in feet. -- If no altitude given, use default (zero altitude = sea level). -- Table gives speed of sound in miles per hour at various altitudes: -- altitude = -17,499 to 402,499 feet -- mach_table[a + 4] = s where -- a = (altitude / 5000) rounded to nearest integer (-3 to 80) -- s = speed of sound (mph) at that altitude -- LATER: Should calculate result from an interpolation between the next -- lower and higher altitudes in table, rather than rounding to nearest. -- From: http://www.aerospaceweb.org/question/atmosphere/q0112.shtml local mach_table = { -- a = 799.5, 787.0, 774.2, 761.207051, -- -3 to 0 748.0, 734.6, 721.0, 707.0, 692.8, 678.3, 663.5, 660.1, 660.1, 660.1, -- 1 to 10 660.1, 660.1, 660.1, 662.0, 664.3, 666.5, 668.9, 671.1, 673.4, 675.6, -- 11 to 20 677.9, 683.7, 689.9, 696.0, 702.1, 708.1, 714.0, 719.9, 725.8, 731.6, -- 21 to 30 737.3, 737.7, 737.7, 736.2, 730.5, 724.6, 718.8, 712.9, 707.0, 701.0, -- 31 to 40 695.0, 688.9, 682.8, 676.6, 670.4, 664.1, 657.8, 652.9, 648.3, 643.7, -- 41 to 50 639.1, 634.4, 629.6, 624.8, 620.0, 615.2, 613.2, 613.2, 613.2, 613.5, -- 51 to 60 614.4, 615.3, 616.7, 619.8, 623.4, 629.7, 635.0, 641.1, 650.6, 660.0, -- 61 to 70 672.5, 674.3, 676.1, 677.9, 679.7, 681.5, 683.3, 685.1, 686.8, 688.6, -- 71 to 80 } altitude = altitude or 0 local a = (altitude < 0) and -altitude or altitude a = floor(a / 5000 + 0.5) if altitude < 0 then a = -a end if a < -3 then a = -3 elseif a > 80 then a = 80 end return mach_table[a + 4] * 0.44704 -- mph converted to m/s end -- END: Code required only for built-in units. ------------------------------------------------------------------------ local function add_style(parms, class) -- Add selected template style to parms if not already present. parms.templatestyles = parms.templatestyles or {} if not parms.templatestyles[class] then parms.templatestyles[class] = parms.frame:extensionTag({ name = 'templatestyles', args = { src = text_code.titles[class] } }) end end local function get_styles(parms) -- Return string of required template styles, empty if none. if parms.templatestyles then local t = {} for _, v in pairs(parms.templatestyles) do table.insert(t, v) end return table.concat(t) end return '' end local function get_range(word) -- Return a range (string or table) corresponding to word (like "to"), -- or return nil if not a range word. local ranges = text_code.ranges return ranges.types[word] or ranges.types[ranges.aliases[word]] end local function check_mismatch(unit1, unit2) -- If unit1 cannot be converted to unit2, return an error message table. -- This allows conversion between units of the same type, and between -- Nm (normally torque) and ftlb (energy), as in gun-related articles. -- This works because Nm is the base unit (scale = 1) for both the -- primary type (torque), and the alternate type (energy, where Nm = J). -- A match occurs if the primary types are the same, or if unit1 matches -- the alternate type of unit2, and vice versa. That provides a whitelist -- of which conversions are permitted between normally incompatible types. if unit1.utype == unit2.utype or (unit1.utype == unit2.alttype and unit1.alttype == unit2.utype) then return nil end return { 'cvt_mismatch', unit1.utype, unit2.utype } end local function override_from(out_table, in_table, fields) -- Copy the specified fields from in_table to out_table, but do not -- copy nil fields (keep any corresponding field in out_table). for _, field in ipairs(fields) do if in_table[field] then out_table[field] = in_table[field] end end end local function shallow_copy(t) -- Return a shallow copy of table t. -- Do not need the features and overhead of the Scribunto mw.clone(). local result = {} for k, v in pairs(t) do result[k] = v end return result end local unit_mt = { -- Metatable to get missing values for a unit that does not accept SI prefixes. -- Warning: The boolean value 'false' is returned for any missing field -- so __index is not called twice for the same field in a given unit. __index = function (self, key) local value if key == 'name1' or key == 'sym_us' then value = self.symbol elseif key == 'name2' then value = self.name1 .. plural_suffix elseif key == 'name1_us' then value = self.name1 if not rawget(self, 'name2_us') then -- If name1_us is 'foot', do not make name2_us by appending plural_suffix. self.name2_us = self.name2 end elseif key == 'name2_us' then local raw1_us = rawget(self, 'name1_us') if raw1_us then value = raw1_us .. plural_suffix else value = self.name2 end elseif key == 'link' then value = self.name1 else value = false end rawset(self, key, value) return value end } local function prefixed_name(unit, name, index) -- Return unit name with SI prefix inserted at correct position. -- index = 1 (name1), 2 (name2), 3 (name1_us), 4 (name2_us). -- The position is a byte (not character) index, so use Lua's sub(). local pos = rawget(unit, 'prefix_position') if type(pos) == 'string' then pos = tonumber(split(pos, ',')[index]) end if pos then return name:sub(1, pos - 1) .. unit.si_name .. name:sub(pos) end return unit.si_name .. name end local unit_prefixed_mt = { -- Metatable to get missing values for a unit that accepts SI prefixes. -- Before use, fields si_name, si_prefix must be defined. -- The unit must define _symbol, _name1 and -- may define _sym_us, _name1_us, _name2_us -- (_sym_us, _name2_us may be defined for a language using sp=us -- to refer to a variant unrelated to U.S. units). __index = function (self, key) local value if key == 'symbol' then value = self.si_prefix .. self._symbol if value == 'l' then value = 'L' end elseif key == 'sym_us' then value = rawget(self, '_sym_us') if value then value = self.si_prefix .. value else value = self.symbol end elseif key == 'name1' then value = prefixed_name(self, self._name1, 1) elseif key == 'name2' then value = rawget(self, '_name2') if value then value = prefixed_name(self, value, 2) else value = self.name1 .. plural_suffix end elseif key == 'name1_us' then value = rawget(self, '_name1_us') if value then value = prefixed_name(self, value, 3) else value = self.name1 end elseif key == 'name2_us' then value = rawget(self, '_name2_us') if value then value = prefixed_name(self, value, 4) elseif rawget(self, '_name1_us') then value = self.name1_us .. plural_suffix else value = self.name2 end elseif key == 'link' then value = self.name1 else value = false end rawset(self, key, value) return value end } local unit_per_mt = { -- Metatable to get values for a per unit of form "x/y". -- This is never called to determine a unit name or link because per units -- are handled as a special case. -- Similarly, the default output is handled elsewhere, and for a symbol -- this is only called from get_default() for default_exceptions. __index = function (self, key) local value if key == 'symbol' then local per = self.per local unit1, unit2 = per[1], per[2] if unit1 then value = unit1[key] .. '/' .. unit2[key] else value = '/' .. unit2[key] end elseif key == 'sym_us' then value = self.symbol elseif key == 'scale' then local per = self.per local unit1, unit2 = per[1], per[2] value = (unit1 and unit1.scale or 1) * self.scalemultiplier / unit2.scale else value = false end rawset(self, key, value) return value end } local function make_per(unitcode, unit_table, ulookup) -- Return true, t where t is a per unit with unit codes expanded to unit tables, -- or return false, t where t is an error message table. local result = { unitcode = unitcode, utype = unit_table.utype, per = {} } override_from(result, unit_table, { 'invert', 'iscomplex', 'default', 'link', 'symbol', 'symlink' }) result.symbol_raw = (result.symbol or false) -- to distinguish between a defined exception and a metatable calculation local prefix for i, v in ipairs(unit_table.per) do if i == 1 and v == '' then -- First unit symbol can be empty; that gives a nil first unit table. elseif i == 1 and text_code.currency[v] then prefix = currency_text or v else local success, t = ulookup(v) if not success then return false, t end result.per[i] = t end end local multiplier = unit_table.multiplier if not result.utype then -- Creating an automatic per unit. local unit1 = result.per[1] local utype = (unit1 and unit1.utype or prefix or '') .. '/' .. result.per[2].utype local t = data_code.per_unit_fixups[utype] if t then if type(t) == 'table' then utype = t.utype or utype result.link = result.link or t.link multiplier = multiplier or t.multiplier else utype = t end end result.utype = utype end result.scalemultiplier = multiplier or 1 result.vprefix = prefix or false -- set to non-nil to avoid calling __index return true, setmetatable(result, unit_per_mt) end local function lookup(parms, unitcode, what, utable, fails, depth) -- Return true, t where t is a copy of the unit's converter table, -- or return false, t where t is an error message table. -- Parameter 'what' determines whether combination units are accepted: -- 'no_combination' : single unit only -- 'any_combination' : single unit or combination or output multiple -- 'only_multiple' : single unit or output multiple only -- Parameter unitcode is a symbol (like 'g'), with an optional SI prefix (like 'kg'). -- If, for example, 'kg' is in this table, that entry is used; -- otherwise the prefix ('k') is applied to the base unit ('g'). -- If unitcode is a known combination code (and if allowed by what), -- a table of output multiple unit tables is included in the result. -- For compatibility with the old template, an underscore in a unitcode is -- replaced with a space so usage like {{convert|350|board_feet}} works. -- Wikignomes may also put two spaces or "&nbsp;" in combinations, so -- replace underscore, "&nbsp;", and multiple spaces with a single space. utable = utable or parms.unittable or all_units fails = fails or {} depth = depth and depth + 1 or 1 if depth > 9 then -- There are ways to mistakenly define units which result in infinite -- recursion when lookup() is called. That gives a long delay and very -- confusing error messages, so the depth parameter is used as a guard. return false, { 'cvt_lookup', unitcode } end if unitcode == nil or unitcode == '' then return false, { 'cvt_no_unit' } end unitcode = unitcode:gsub('_', ' '):gsub('&nbsp;', ' '):gsub(' +', ' ') local function call_make_per(t) return make_per(unitcode, t, function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end ) end local t = utable[unitcode] if t then if t.shouldbe then return false, { 'cvt_should_be', t.shouldbe } end if t.sp_us then parms.opt_sp_us = true end local target = t.target -- nil, or unitcode is an alias for this target if target then local success, result = lookup(parms, target, what, utable, fails, depth) if not success then return false, result end override_from(result, t, { 'customary', 'default', 'link', 'symbol', 'symlink' }) local multiplier = t.multiplier if multiplier then result.multiplier = tostring(multiplier) result.scale = result.scale * multiplier end return true, result end if t.per then return call_make_per(t) end local combo = t.combination -- nil or a table of unitcodes if combo then local multiple = t.multiple if what == 'no_combination' or (what == 'only_multiple' and not multiple) then return false, { 'cvt_bad_unit', unitcode } end -- Recursively create a combination table containing the -- converter table of each unitcode. local result = { utype = t.utype, multiple = multiple, combination = {} } local cvt = result.combination for i, v in ipairs(combo) do local success, t = lookup(parms, v, multiple and 'no_combination' or 'only_multiple', utable, fails, depth) if not success then return false, t end cvt[i] = t end return true, result end local result = shallow_copy(t) result.unitcode = unitcode if result.prefixes then result.si_name = '' result.si_prefix = '' return true, setmetatable(result, unit_prefixed_mt) end return true, setmetatable(result, unit_mt) end local SIprefixes = text_code.SIprefixes for plen = SIprefixes[1] or 2, 1, -1 do -- Look for an SI prefix; should never occur with an alias. -- Check for longer prefix first ('dam' is decametre). -- SIprefixes[1] = prefix maximum #characters (as seen by mw.ustring.sub). local prefix = usub(unitcode, 1, plen) local si = SIprefixes[prefix] if si then local t = utable[usub(unitcode, plen+1)] if t and t.prefixes then local result = shallow_copy(t) result.unitcode = unitcode result.si_name = parms.opt_sp_us and si.name_us or si.name result.si_prefix = si.prefix or prefix result.scale = t.scale * 10 ^ (si.exponent * t.prefixes) return true, setmetatable(result, unit_prefixed_mt) end end end -- Accept user-defined combinations like "acre+m2+ha" or "acre m2 ha" for output. -- If '+' is used, each unit code can include a space, and any error is fatal. -- If ' ' is used and if each space-separated word is a unit code, it is a combo, -- but errors are not fatal so the unit code can be looked up as an extra unit. local err_is_fatal local combo = collection() if unitcode:find('+', 1, true) then err_is_fatal = true for item in (unitcode .. '+'):gmatch('%s*(.-)%s*%+') do if item ~= '' then combo:add(item) end end elseif unitcode:find('%s') then for item in unitcode:gmatch('%S+') do combo:add(item) end end if combo.n > 1 then local function lookup_combo() if what == 'no_combination' or what == 'only_multiple' then return false, { 'cvt_bad_unit', unitcode } end local result = { combination = {} } local cvt = result.combination for i, v in ipairs(combo) do local success, t = lookup(parms, v, 'only_multiple', utable, fails, depth) if not success then return false, t end if i == 1 then result.utype = t.utype else local mismatch = check_mismatch(result, t) if mismatch then return false, mismatch end end cvt[i] = t end return true, result end local success, result = lookup_combo() if success or err_is_fatal then return success, result end end -- Accept any unit with an engineering notation prefix like "e6cuft" -- (million cubic feet), but not chained prefixes like "e3e6cuft", -- and not if the unit is a combination or multiple, -- and not if the unit has an offset or is a built-in. -- Only en digits are accepted. local exponent, baseunit = unitcode:match('^e(%d+)(.*)') if exponent then local engscale = text_code.eng_scales[exponent] if engscale then local success, result = lookup(parms, baseunit, 'no_combination', utable, fails, depth) if success and not (result.offset or result.builtin or result.engscale) then result.unitcode = unitcode -- 'e6cuft' not 'cuft' result.defkey = unitcode -- key to lookup default exception result.engscale = engscale result.scale = result.scale * 10 ^ tonumber(exponent) return true, result end end end -- Look for x/y; split on right-most slash to get scale correct (x/y/z is x/y per z). local top, bottom = unitcode:match('^(.-)/([^/]+)$') if top and not unitcode:find('e%d') then -- If valid, create an automatic per unit for an "x/y" unit code. -- The unitcode must not include extraneous spaces. -- Engineering notation (apart from at start and which has been stripped before here), -- is not supported so do not make a per unit if find text like 'e3' in unitcode. local success, result = call_make_per({ per = {top, bottom} }) if success then return true, result end end if not parms.opt_ignore_error and not get_range(unitcode) then -- Want the "what links here" list for the extra_module to show only cases -- where an extra unit is used, so do not require it if invoked from {{val}} -- or if looking up a range word which cannot be a unit. if not extra_units then local success, extra = pcall(function () return require(extra_module).extra_units end) if success and type(extra) == 'table' then extra_units = extra end end if extra_units then -- A unit in one data table might refer to a unit in the other table, so -- switch between them, relying on fails or depth to terminate loops. if not fails[unitcode] then fails[unitcode] = true local other = (utable == all_units) and extra_units or all_units local success, result = lookup(parms, unitcode, what, other, fails, depth) if success then return true, result end end end end if to_en_table then -- At fawiki it is common to translate all digits so a unit like "km2" becomes "km۲". local en_code = ustring.gsub(unitcode, '%d', to_en_table) if en_code ~= unitcode then return lookup(parms, en_code, what, utable, fails, depth) end end return false, { 'cvt_unknown', unitcode } end local function valid_number(num) -- Return true if num is a valid number. -- In Scribunto (different from some standard Lua), when expressed as a string, -- overflow or other problems are indicated with text like "inf" or "nan" -- which are regarded as invalid here (each contains "n"). if type(num) == 'number' and tostring(num):find('n', 1, true) == nil then return true end end local function hyphenated(name, parts) -- Return a hyphenated form of given name (for adjectival usage). -- The name may be linked and the target of the link must not be changed. -- Hypothetical examples: -- [[long ton|ton]] → [[long ton|ton]] (no change) -- [[tonne|long ton]] → [[tonne|long-ton]] -- [[metric ton|long ton]] → [[metric ton|long-ton]] -- [[long ton]] → [[long ton|long-ton]] -- Input can also have multiple links in a single name like: -- [[United States customary units|U.S.]] [[US gallon|gallon]] -- [[mile]]s per [[United States customary units|U.S.]] [[quart]] -- [[long ton]]s per [[short ton]] -- Assume that links cannot be nested (never like "[[abc[[def]]ghi]]"). -- This uses a simple and efficient procedure that works for most cases. -- Some units (if used) would require more, and can later think about -- adding a method to handle exceptions. -- The procedure is to replace each space with a hyphen, but -- not a space after ')' [for "(pre-1954&nbsp;US) nautical mile"], and -- not spaces immediately before '(' or in '(...)' [for cases like -- "British thermal unit (ISO)" and "Calorie (International Steam Table)"]. if name:find(' ', 1, true) then if parts then local pos if name:sub(1, 1) == '(' then pos = name:find(')', 1, true) if pos then return name:sub(1, pos+1) .. name:sub(pos+2):gsub(' ', '-') end elseif name:sub(-1) == ')' then pos = name:find('(', 1, true) if pos then return name:sub(1, pos-2):gsub(' ', '-') .. name:sub(pos-1) end end return name:gsub(' ', '-') end parts = collection() for before, item, after in name:gmatch('([^[]*)(%[%[[^[]*%]%])([^[]*)') do if item:find(' ', 1, true) then local prefix local plen = item:find('|', 1, true) if plen then prefix = item:sub(1, plen) item = item:sub(plen + 1, -3) else prefix = item:sub(1, -3) .. '|' item = item:sub(3, -3) end item = prefix .. hyphenated(item, parts) .. ']]' end parts:add(before:gsub(' ', '-') .. item .. after:gsub(' ', '-')) end if parts.n == 0 then -- No link like "[[...]]" was found in the original name. parts:add(hyphenated(name, parts)) end return table.concat(parts) end return name end local function hyphenated_maybe(parms, want_name, sep, id, inout) -- Return s, f where -- s = id, possibly modified -- f = true if hyphenated -- Possible modifications: hyphenate; prepend '-'; append mid text. if id == nil or id == '' then return '' end local mid = (inout == (parms.opt_flip and 'out' or 'in')) and parms.mid or '' if want_name then if parms.opt_adjectival then return '-' .. hyphenated(id) .. mid, true end if parms.opt_add_s and id:sub(-1) ~= 's' then id = id .. 's' -- for nowiki end end return sep .. id .. mid end local function use_minus(text) -- Return text with Unicode minus instead of '-', if present. if text:sub(1, 1) == '-' then return MINUS .. text:sub(2) end return text end local function digit_groups(parms, text, method) -- Return a numbered table of groups of digits (left-to-right, in local language). -- Parameter method is a number or nil: -- 3 for 3-digit grouping (default), or -- 2 for 3-then-2 grouping (only for digits before decimal mark). local len_right local len_left = text:find('.', 1, true) if len_left then len_right = #text - len_left len_left = len_left - 1 else len_left = #text end local twos = method == 2 and len_left > 5 local groups = collection() local run = len_left local n if run < 4 or (run == 4 and parms.opt_comma5) then if parms.opt_gaps then n = run else n = #text end elseif twos then n = run % 2 == 0 and 1 or 2 else n = run % 3 == 0 and 3 or run % 3 end while run > 0 do groups:add(n) run = run - n n = (twos and run > 3) and 2 or 3 end if len_right then if groups.n == 0 then groups:add(0) end if parms.opt_gaps and len_right > 3 then local want4 = not parms.opt_gaps3 -- true gives no gap before trailing single digit local isfirst = true run = len_right while run > 0 do n = (want4 and run == 4) and 4 or (run > 3 and 3 or run) if isfirst then isfirst = false groups[groups.n] = groups[groups.n] + 1 + n else groups:add(n) end run = run - n end else groups[groups.n] = groups[groups.n] + 1 + len_right end end local pos = 1 for i, length in ipairs(groups) do groups[i] = from_en(text:sub(pos, pos + length - 1)) pos = pos + length end return groups end function with_separator(parms, text) -- for forward declaration above -- Input text is a number in en digits with optional '.' decimal mark. -- Return an equivalent, formatted for display: -- with a custom decimal mark instead of '.', if wanted -- with thousand separators inserted, if wanted -- digits in local language -- The given text is like '123' or '123.' or '12345.6789'. -- The text has no sign (caller inserts that later, if necessary). -- When using gaps, they are inserted before and after the decimal mark. -- Separators are inserted only before the decimal mark. -- A trailing dot (as in '123.') is removed because their use appears to -- be accidental, and such a number should be shown as '123' or '123.0'. -- It is useful for convert to suppress the dot so, for example, '4000.' -- is a simple way of indicating that all the digits are significant. if text:sub(-1) == '.' then text = text:sub(1, -2) end if #text < 4 or parms.opt_nocomma or numsep == '' then return from_en(text) end local groups = digit_groups(parms, text, group_method) if parms.opt_gaps then if groups.n <= 1 then return groups[1] or '' end local nowrap = '<span style="white-space: nowrap">' local gap = '<span style="margin-left: 0.25em">' local close = '</span>' return nowrap .. groups[1] .. gap .. table.concat(groups, close .. gap, 2, groups.n) .. close .. close end return table.concat(groups, numsep) end -- An input value like 1.23e12 is displayed using scientific notation (1.23×10¹²). -- That also makes the output use scientific notation, except for small values. -- In addition, very small or very large output values use scientific notation. -- Use format(fmtpower, significand, '10', exponent) where each argument is a string. local fmtpower = '%s<span style="margin:0 .15em 0 .25em">×</span>%s<sup>%s</sup>' local function with_exponent(parms, show, exponent) -- Return wikitext to display the implied value in scientific notation. -- Input uses en digits; output uses digits in local language. return format(fmtpower, with_separator(parms, show), from_en('10'), use_minus(from_en(tostring(exponent)))) end local function make_sigfig(value, sigfig) -- Return show, exponent that are equivalent to the result of -- converting the number 'value' (where value >= 0) to a string, -- rounded to 'sigfig' significant figures. -- The returned items are: -- show: a string of digits; no sign and no dot; -- there is an implied dot before show. -- exponent: a number (an integer) to shift the implied dot. -- Resulting value = tonumber('.' .. show) * 10^exponent. -- Examples: -- make_sigfig(23.456, 3) returns '235', 2 (.235 * 10^2). -- make_sigfig(0.0023456, 3) returns '235', -2 (.235 * 10^-2). -- make_sigfig(0, 3) returns '000', 1 (.000 * 10^1). if sigfig <= 0 then sigfig = 1 elseif sigfig > maxsigfig then sigfig = maxsigfig end if value == 0 then return string.rep('0', sigfig), 1 end local exp, fracpart = math.modf(log10(value)) if fracpart >= 0 then fracpart = fracpart - 1 exp = exp + 1 end local digits = format('%.0f', 10^(fracpart + sigfig)) if #digits > sigfig then -- Overflow (for sigfig=3: like 0.9999 rounding to "1000"; need "100"). digits = digits:sub(1, sigfig) exp = exp + 1 end assert(#digits == sigfig, 'Bug: rounded number has wrong length') return digits, exp end -- Fraction output format. local fracfmt = { { -- Like {{frac}} (fraction slash). '<span class="frac" role="math">{SIGN}<span class="num">{NUM}</span>&frasl;<span class="den">{DEN}</span></span>', -- 1/2 '<span class="frac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="num">{NUM}</span>&frasl;<span class="den">{DEN}</span></span>', -- 1+2/3 style = 'frac', }, { -- Like {{sfrac}} (stacked fraction, that is, horizontal bar). '<span class="sfrac tion" role="math">{SIGN}<span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span>', -- 1//2 '<span class="sfrac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="tion"><span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span></span>', -- 1+2//3 style = 'sfrac', }, } local function format_fraction(parms, inout, negative, wholestr, numstr, denstr, do_spell, style) -- Return wikitext for a fraction, possibly spelled. -- Inputs use en digits and have no sign; output uses digits in local language. local wikitext if not style then style = parms.opt_fraction_horizontal and 2 or 1 end if wholestr == '' then wholestr = nil end local substitute = { SIGN = negative and MINUS or '', WHOLE = wholestr and with_separator(parms, wholestr), NUM = from_en(numstr), DEN = from_en(denstr), } wikitext = fracfmt[style][wholestr and 2 or 1]:gsub('{(%u+)}', substitute) if do_spell then if negative then if wholestr then wholestr = '-' .. wholestr else numstr = '-' .. numstr end end local s = spell_number(parms, inout, wholestr, numstr, denstr) if s then return s end end add_style(parms, fracfmt[style].style) return wikitext end local function format_number(parms, show, exponent, isnegative) -- Parameter show is a string or a table containing strings. -- Each string is a formatted number in en digits and optional '.' decimal mark. -- A table represents a fraction: integer, numerator, denominator; -- if a table is given, exponent must be nil. -- Return t where t is a table with fields: -- show = wikitext formatted to display implied value -- (digits in local language) -- is_scientific = true if show uses scientific notation -- clean = unformatted show (possibly adjusted and with inserted '.') -- (en digits) -- sign = '' or MINUS -- exponent = exponent (possibly adjusted) -- The clean and exponent fields can be used to calculate the -- rounded absolute value, if needed. -- -- The value implied by the arguments is found from: -- exponent is nil; and -- show is a string of digits (no sign), with an optional dot; -- show = '123.4' is value 123.4, '1234' is value 1234.0; -- or: -- exponent is an integer indicating where dot should be; -- show is a string of digits (no sign and no dot); -- there is an implied dot before show; -- show does not start with '0'; -- show = '1234', exponent = 3 is value 0.1234*10^3 = 123.4. -- -- The formatted result: -- * Is for an output value and is spelled if wanted and possible. -- * Includes a Unicode minus if isnegative and not spelled. -- * Uses a custom decimal mark, if wanted. -- * Has digits grouped where necessary, if wanted. -- * Uses scientific notation if requested, or for very small or large values -- (which forces result to not be spelled). -- * Has no more than maxsigfig significant digits -- (same as old template and {{#expr}}). local xhi, xlo -- these control when scientific notation (exponent) is used if parms.opt_scientific then xhi, xlo = 4, 2 -- default for output if input uses e-notation elseif parms.opt_scientific_always then xhi, xlo = 0, 0 -- always use scientific notation (experimental) else xhi, xlo = 10, 4 -- default end local sign = isnegative and MINUS or '' local maxlen = maxsigfig local tfrac if type(show) == 'table' then tfrac = show show = tfrac.wholestr assert(exponent == nil, 'Bug: exponent given with fraction') end if not tfrac and not exponent then local integer, dot, decimals = show:match('^(%d*)(%.?)(.*)') if integer == '0' or integer == '' then local zeros, figs = decimals:match('^(0*)([^0]?.*)') if #figs == 0 then if #zeros > maxlen then show = '0.' .. zeros:sub(1, maxlen) end elseif #zeros >= xlo then show = figs exponent = -#zeros elseif #figs > maxlen then show = '0.' .. zeros .. figs:sub(1, maxlen) end elseif #integer >= xhi then show = integer .. decimals exponent = #integer else maxlen = maxlen + #dot if #show > maxlen then show = show:sub(1, maxlen) end end end if exponent then local function zeros(n) return string.rep('0', n) end if #show > maxlen then show = show:sub(1, maxlen) end if exponent > xhi or exponent <= -xlo or (exponent == xhi and show ~= '1' .. zeros(xhi - 1)) then -- When xhi, xlo = 10, 4 (the default), scientific notation is used if the -- rounded value satisfies: value >= 1e9 or value < 1e-4 (1e9 = 0.1e10), -- except if show is '1000000000' (1e9), for example: -- {{convert|1000000000|m|m|sigfig=10}} → 1,000,000,000 metres (1,000,000,000 m) local significand if #show > 1 then significand = show:sub(1, 1) .. '.' .. show:sub(2) else significand = show end return { clean = '.' .. show, exponent = exponent, sign = sign, show = sign .. with_exponent(parms, significand, exponent-1), is_scientific = true, } end if exponent >= #show then show = show .. zeros(exponent - #show) -- result has no dot elseif exponent <= 0 then show = '0.' .. zeros(-exponent) .. show else show = show:sub(1, exponent) .. '.' .. show:sub(exponent+1) end end local formatted_show if tfrac then show = tostring(tfrac.value) -- to set clean in returned table formatted_show = format_fraction(parms, 'out', isnegative, tfrac.wholestr, tfrac.numstr, tfrac.denstr, parms.opt_spell_out) else if isnegative and show:match('^0.?0*$') then sign = '' -- don't show minus if result is negative but rounds to zero end formatted_show = sign .. with_separator(parms, show) if parms.opt_spell_out then formatted_show = spell_number(parms, 'out', sign .. show) or formatted_show end end return { clean = show, sign = sign, show = formatted_show, is_scientific = false, -- to avoid calling __index } end local function extract_fraction(parms, text, negative) -- If text represents a fraction, return -- value, altvalue, show, denominator -- where -- value is a number (value of the fraction in argument text) -- altvalue is an alternate interpretation of any fraction for the hands -- unit where "12.1+3/4" means 12 hands 1.75 inches -- show is a string (formatted text for display of an input value, -- and is spelled if wanted and possible) -- denominator is value of the denominator in the fraction -- Otherwise, return nil. -- Input uses en digits and '.' decimal mark (input has been translated). -- Output uses digits in local language and local decimal mark, if any. ------------------------------------------------------------------------ -- Originally this function accepted x+y/z where x, y, z were any valid -- numbers, possibly with a sign. For example '1.23e+2+1.2/2.4' = 123.5, -- and '2-3/8' = 1.625. However, such usages were found to be errors or -- misunderstandings, so since August 2014 the following restrictions apply: -- x (if present) is an integer or has a single digit after decimal mark -- y and z are unsigned integers -- e-notation is not accepted -- The overall number can start with '+' or '-' (so '12+3/4' and '+12+3/4' -- and '-12-3/4' are valid). -- Any leading negative sign is removed by the caller, so only inputs -- like the following are accepted here (may have whitespace): -- negative = false false true (there was a leading '-') -- text = '2/3' '+2/3' '2/3' -- text = '1+2/3' '+1+2/3' '1-2/3' -- text = '12.3+1/2' '+12.3+1/2' '12.3-1/2' -- Values like '12.3+1/2' are accepted, but are intended only for use -- with the hands unit (not worth adding code to enforce that). ------------------------------------------------------------------------ local leading_plus, prefix, numstr, slashes, denstr = text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*(/+)%s*(%d+)%s*$') if not leading_plus then -- Accept a single U+2044 fraction slash because that may be pasted. leading_plus, prefix, numstr, denstr = text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*⁄%s*(%d+)%s*$') slashes = '/' end local numerator = tonumber(numstr) local denominator = tonumber(denstr) if numerator == nil or denominator == nil or (negative and leading_plus ~= '') then return nil end local whole, wholestr if prefix == '' then wholestr = '' whole = 0 else -- Any prefix must be like '12+' or '12-' (whole number and fraction sign); -- '12.3+' and '12.3-' are also accepted (single digit after decimal point) -- because '12.3+1/2 hands' is valid (12 hands 3½ inches). local num1, num2, frac_sign = prefix:match('^(%d+)(%.?%d?)%s*([+%-])$') if num1 == nil then return nil end if num2 == '' then -- num2 must be '' or like '.1' but not '.' or '.12' wholestr = num1 else if #num2 ~= 2 then return nil end wholestr = num1 .. num2 end if frac_sign ~= (negative and '-' or '+') then return nil end whole = tonumber(wholestr) if whole == nil then return nil end end local value = whole + numerator / denominator if not valid_number(value) then return nil end local altvalue = whole + numerator / (denominator * 10) local style = #slashes -- kludge: 1 or 2 slashes can be used to select style if style > 2 then style = 2 end local wikitext = format_fraction(parms, 'in', negative, leading_plus .. wholestr, numstr, denstr, parms.opt_spell_in, style) return value, altvalue, wikitext, denominator end local function extract_number(parms, text, another, no_fraction) -- Return true, info if can extract a number from text, -- where info is a table with the result, -- or return false, t where t is an error message table. -- Input can use en digits or digits in local language and can -- have references at the end. Accepting references is intended -- for use in infoboxes with a field for a value passed to convert. -- Parameter another = true if the expected value is not the first. -- Before processing, the input text is cleaned: -- * Any thousand separators (valid or not) are removed. -- * Any sign is replaced with '-' (if negative) or '' (otherwise). -- That replaces Unicode minus with '-'. -- If successful, the returned info table contains named fields: -- value = a valid number -- altvalue = a valid number, usually same as value but different -- if fraction used (for hands unit) -- singular = true if value is 1 or -1 (to use singular form of units) -- clean = cleaned text with any separators and sign removed -- (en digits and '.' decimal mark) -- show = text formatted for output, possibly with ref strip markers -- (digits in local language and custom decimal mark) -- The resulting show: -- * Is for an input value and is spelled if wanted and possible. -- * Has a rounded value, if wanted. -- * Has digits grouped where necessary, if wanted. -- * If negative, a Unicode minus is used; otherwise the sign is -- '+' (if the input text used '+'), or is '' (if no sign in input). text = strip(text or '') local reference local pos = text:find('\127', 1, true) if pos then local before = text:sub(1, pos - 1) local remainder = text:sub(pos) local refs = {} while #remainder > 0 do local ref, spaces ref, spaces, remainder = remainder:match('^(\127[^\127]*UNIQ[^\127]*%-ref[^\127]*\127)(%s*)(.*)') if ref then table.insert(refs, ref) else refs = {} break end end if #refs > 0 then text = strip(before) reference = table.concat(refs) end end local clean = to_en(text, parms) if clean == '' then return false, { another and 'cvt_no_num2' or 'cvt_no_num' } end local isnegative, propersign = false, '' -- most common case local singular, show, denominator local value = tonumber(clean) local altvalue if value then local sign = clean:sub(1, 1) if sign == '+' or sign == '-' then propersign = (sign == '+') and '+' or MINUS clean = clean:sub(2) end if value < 0 then isnegative = true value = -value end else local valstr for _, prefix in ipairs({ '-', MINUS, '&minus;' }) do -- Including '-' sets isnegative in case input is a fraction like '-2-3/4'. local plen = #prefix if clean:sub(1, plen) == prefix then valstr = clean:sub(plen + 1) if valstr:match('^%s') then -- "- 1" is invalid but "-1 - 1/2" is ok return false, { 'cvt_bad_num', text } end break end end if valstr then isnegative = true propersign = MINUS clean = valstr value = tonumber(clean) end if value == nil then if not no_fraction then value, altvalue, show, denominator = extract_fraction(parms, clean, isnegative) end if value == nil then return false, { 'cvt_bad_num', text } end if value <= 1 then singular = true -- for example, "½ mile" or "one half mile" (singular unit) end end end if not valid_number(value) then -- for example, "1e310" may overflow return false, { 'cvt_invalid_num' } end if show == nil then -- clean is a non-empty string with no spaces, and does not represent a fraction, -- and value = tonumber(clean) is a number >= 0. -- If the input uses e-notation, show will be displayed using a power of ten, but -- we use the number as given so it might not be normalized scientific notation. -- The input value is spelled if specified so any e-notation is ignored; -- that allows input like 2e6 to be spelled as "two million" which works -- because the spell module converts '2e6' to '2000000' before spelling. local function rounded(value, default, exponent) local precision = parms.opt_ri if precision then local fmt = '%.' .. format('%d', precision) .. 'f' local result = fmt:format(tonumber(value) + 2e-14) -- fudge for some common cases of bad rounding if not exponent then singular = (tonumber(result) == 1) end return result end return default end singular = (value == 1) local scientific local significand, exponent = clean:match('^([%d.]+)[Ee]([+%-]?%d+)') if significand then show = with_exponent(parms, rounded(significand, significand, exponent), exponent) scientific = true else show = with_separator(parms, rounded(value, clean)) end show = propersign .. show if parms.opt_spell_in then show = spell_number(parms, 'in', propersign .. rounded(value, clean)) or show scientific = false end if scientific then parms.opt_scientific = true end end if isnegative and (value ~= 0) then value = -value altvalue = -(altvalue or value) end return true, { value = value, altvalue = altvalue or value, singular = singular, clean = clean, show = show .. (reference or ''), denominator = denominator, } end local function get_number(text) -- Return v, f where: -- v = nil (text is not a number) -- or -- v = value of text (text is a number) -- f = true if value is an integer -- Input can use en digits or digits in local language or separators, -- but no Unicode minus, and no fraction. if text then local number = tonumber(to_en(text)) if number then local _, fracpart = math.modf(number) return number, (fracpart == 0) end end end local function gcd(a, b) -- Return the greatest common denominator for the given values, -- which are known to be positive integers. if a > b then a, b = b, a end if a <= 0 then return b end local r = b % a if r <= 0 then return a end if r == 1 then return 1 end return gcd(r, a) end local function fraction_table(value, denominator) -- Return value as a string or a table: -- * If result is a string, there is no fraction, and the result -- is value formatted as a string of en digits. -- * If result is a table, it represents a fraction with named fields: -- wholestr, numstr, denstr (strings of en digits for integer, numerator, denominator). -- The result is rounded to the nearest multiple of (1/denominator). -- If the multiple is zero, no fraction is included. -- No fraction is included if value is very large as the fraction would -- be unhelpful, particularly if scientific notation is required. -- Input value is a non-negative number. -- Input denominator is a positive integer for the desired fraction. if value <= 0 then return '0' end if denominator <= 0 or value > 1e8 then return format('%.2f', value) end local integer, decimals = math.modf(value) local numerator = floor((decimals * denominator) + 0.5 + 2e-14) -- add fudge for some common cases of bad rounding if numerator >= denominator then integer = integer + 1 numerator = 0 end local wholestr = tostring(integer) if numerator > 0 then local div = gcd(numerator, denominator) if div > 1 then numerator = numerator / div denominator = denominator / div end return { wholestr = (integer > 0) and wholestr or '', numstr = tostring(numerator), denstr = tostring(denominator), value = value, } end return wholestr end local function preunits(count, preunit1, preunit2) -- If count is 1: -- ignore preunit2 -- return p1 -- else: -- preunit1 is used for preunit2 if the latter is empty -- return p1, p2 -- where: -- p1 is text to insert before the input unit -- p2 is text to insert before the output unit -- p1 or p2 may be nil to mean "no preunit" -- Using '+' gives output like "5+ feet" (no space before, but space after). local function withspace(text, wantboth) -- Return text with space before and, if wantboth, after. -- However, no space is added if there is a space or '&nbsp;' or '-' -- at that position ('-' is for adjectival text). -- There is also no space if text starts with '&' -- (e.g. '&deg;' would display a degree symbol with no preceding space). local char = text:sub(1, 1) if char == '&' then return text -- an html entity can be used to specify the exact display end if not (char == ' ' or char == '-' or char == '+') then text = ' ' .. text end if wantboth then char = text:sub(-1, -1) if not (char == ' ' or char == '-' or text:sub(-6, -1) == '&nbsp;') then text = text .. ' ' end end return text end local PLUS = '+ ' preunit1 = preunit1 or '' local trim1 = strip(preunit1) if count == 1 then if trim1 == '' then return nil end if trim1 == '+' then return PLUS end return withspace(preunit1, true) end preunit1 = withspace(preunit1) preunit2 = preunit2 or '' local trim2 = strip(preunit2) if trim1 == '+' then if trim2 == '' or trim2 == '+' then return PLUS, PLUS end preunit1 = PLUS end if trim2 == '' then if trim1 == '' then return nil, nil end preunit2 = preunit1 elseif trim2 == '+' then preunit2 = PLUS elseif trim2 == '&#32;' then -- trick to make preunit2 empty preunit2 = nil else preunit2 = withspace(preunit2) end return preunit1, preunit2 end local function range_text(range, want_name, parms, before, after, inout, options) -- Return before .. rtext .. after -- where rtext is the text that separates two values in a range. local rtext, adj_text, exception options = options or {} if type(range) == 'table' then -- Table must specify range text for ('off' and 'on') or ('input' and 'output'), -- and may specify range text for 'adj=on', -- and may specify exception = true. rtext = range[want_name and 'off' or 'on'] or range[((inout == 'in') == (parms.opt_flip == true)) and 'output' or 'input'] adj_text = range['adj'] exception = range['exception'] else rtext = range end if parms.opt_adjectival then if want_name or (exception and parms.abbr_org == 'on') then rtext = adj_text or rtext:gsub(' ', '-'):gsub('&nbsp;', '-') end end if rtext == '–' and (options.spaced or after:sub(1, #MINUS) == MINUS) then rtext = '&nbsp;– ' end return before .. rtext .. after end local function get_composite(parms, iparm, in_unit_table) -- Look for a composite input unit. For example, {{convert|1|yd|2|ft|3|in}} -- would result in a call to this function with -- iparm = 3 (parms[iparm] = "2", just after the first unit) -- in_unit_table = (unit table for "yd"; contains value 1 for number of yards) -- Return true, iparm, unit where -- iparm = index just after the composite units (7 in above example) -- unit = composite unit table holding all input units, -- or return true if no composite unit is present in parms, -- or return false, t where t is an error message table. local default, subinfo local composite_units, count = { in_unit_table }, 1 local fixups = {} local total = in_unit_table.valinfo[1].value local subunit = in_unit_table while subunit.subdivs do -- subdivs is nil or a table of allowed subdivisions local subcode = strip(parms[iparm+1]) local subdiv = subunit.subdivs[subcode] or subunit.subdivs[(all_units[subcode] or {}).target] if not subdiv then break end local success success, subunit = lookup(parms, subcode, 'no_combination') if not success then return false, subunit end -- should never occur success, subinfo = extract_number(parms, parms[iparm]) if not success then return false, subinfo end iparm = iparm + 2 subunit.inout = 'in' subunit.valinfo = { subinfo } -- Recalculate total as a number of subdivisions. -- subdiv[1] = number of subdivisions per previous unit (integer > 1). total = total * subdiv[1] + subinfo.value if not default then -- set by the first subdiv with a default defined default = subdiv.default end count = count + 1 composite_units[count] = subunit if subdiv.unit or subdiv.name then fixups[count] = { unit = subdiv.unit, name = subdiv.name, valinfo = subunit.valinfo } end end if count == 1 then return true -- no error and no composite unit end for i, fixup in pairs(fixups) do local unit = fixup.unit local name = fixup.name if not unit or (count > 2 and name) then composite_units[i].fixed_name = name else local success, alternate = lookup(parms, unit, 'no_combination') if not success then return false, alternate end -- should never occur alternate.inout = 'in' alternate.valinfo = fixup.valinfo composite_units[i] = alternate end end return true, iparm, { utype = in_unit_table.utype, scale = subunit.scale, -- scale of last (least significant) unit valinfo = { { value = total, clean = subinfo.clean, denominator = subinfo.denominator } }, composite = composite_units, default = default or in_unit_table.default } end local function translate_parms(parms, kv_pairs) -- Update fields in parms by translating each key:value in kv_pairs to terms -- used by this module (may involve translating from local language to English). -- Also, checks are performed which may display warnings, if enabled. -- Return true if successful or return false, t where t is an error message table. currency_text = nil -- local testing can hold module in memory; must clear globals if kv_pairs.adj and kv_pairs.sing then -- For enwiki (before translation), warn if attempt to use adj and sing -- as the latter is a deprecated alias for the former. if kv_pairs.adj ~= kv_pairs.sing and kv_pairs.sing ~= '' then add_warning(parms, 1, 'cvt_unknown_option', 'sing=' .. kv_pairs.sing) end kv_pairs.sing = nil end kv_pairs.comma = kv_pairs.comma or config.comma -- for plwiki who want default comma=5 for loc_name, loc_value in pairs(kv_pairs) do local en_name = text_code.en_option_name[loc_name] if en_name then local en_value = text_code.en_option_value[en_name] if en_value == 'INTEGER' then -- altitude_ft, altitude_m, frac, sigfig en_value = nil if loc_value == '' then add_warning(parms, 2, 'cvt_empty_option', loc_name) else local minimum local number, is_integer = get_number(loc_value) if en_name == 'sigfig' then minimum = 1 elseif en_name == 'frac' then minimum = 2 if number and number < 0 then parms.opt_fraction_horizontal = true number = -number end else minimum = -1e6 end if number and is_integer and number >= minimum then en_value = number else local m if en_name == 'frac' then m = 'cvt_bad_frac' elseif en_name == 'sigfig' then m = 'cvt_bad_sigfig' else m = 'cvt_bad_altitude' end add_warning(parms, 1, m, loc_name .. '=' .. loc_value) end end elseif en_value == 'TEXT' then -- $, input, qid, qual, stylein, styleout, tracking en_value = loc_value ~= '' and loc_value or nil -- accept non-empty user text with no validation if not en_value and (en_name == '$' or en_name == 'qid' or en_name == 'qual') then add_warning(parms, 2, 'cvt_empty_option', loc_name) elseif en_name == '$' then -- Value should be a single character like "€" for the euro currency symbol, but anything is accepted. currency_text = (loc_value == 'euro') and '€' or loc_value elseif en_name == 'input' then -- May have something like {{convert|input=}} (empty input) if source is an infobox -- with optional fields. In that case, want to output nothing rather than an error. parms.input_text = loc_value -- keep input because parms.input is nil if loc_value == '' end else en_value = en_value[loc_value] if en_value and en_value:sub(-1) == '?' then en_value = en_value:sub(1, -2) add_warning(parms, -1, 'cvt_deprecated', loc_name .. '=' .. loc_value) end if en_value == nil then if loc_value == '' then add_warning(parms, 2, 'cvt_empty_option', loc_name) else add_warning(parms, 1, 'cvt_unknown_option', loc_name .. '=' .. loc_value) end elseif en_value == '' then en_value = nil -- an ignored option like adj=off elseif type(en_value) == 'string' and en_value:sub(1, 4) == 'opt_' then for _, v in ipairs(split(en_value, ',')) do local lhs, rhs = v:match('^(.-)=(.+)$') if rhs then parms[lhs] = tonumber(rhs) or rhs else parms[v] = true end end en_value = nil end end parms[en_name] = en_value else add_warning(parms, 1, 'cvt_unknown_option', loc_name .. '=' .. loc_value) end end local abbr_entered = parms.abbr local cfg_abbr = config.abbr if cfg_abbr then -- Don't warn if invalid because every convert would show that warning. if cfg_abbr == 'on always' then parms.abbr = 'on' elseif cfg_abbr == 'off always' then parms.abbr = 'off' elseif parms.abbr == nil then if cfg_abbr == 'on default' then parms.abbr = 'on' elseif cfg_abbr == 'off default' then parms.abbr = 'off' end end end if parms.abbr then if parms.abbr == 'unit' then parms.abbr = 'on' parms.number_word = true end parms.abbr_org = parms.abbr -- original abbr, before any flip elseif parms.opt_hand_hh then parms.abbr_org = 'on' parms.abbr = 'on' else parms.abbr = 'out' -- default is to abbreviate output only (use symbol, not name) end if parms.opt_order_out then -- Disable options that do not work in a useful way with order=out. parms.opt_flip = nil -- override adj=flip parms.opt_spell_in = nil parms.opt_spell_out = nil parms.opt_spell_upper = nil end if parms.opt_spell_out and not abbr_entered then parms.abbr = 'off' -- should show unit name when spelling the output value end if parms.opt_flip then local function swap_in_out(option) local value = parms[option] if value == 'in' then parms[option] = 'out' elseif value == 'out' then parms[option] = 'in' end end swap_in_out('abbr') swap_in_out('lk') if parms.opt_spell_in and not parms.opt_spell_out then -- For simplicity, and because it does not appear to be needed, -- user cannot set an option to spell the output only. parms.opt_spell_in = nil parms.opt_spell_out = true end end if parms.opt_spell_upper then parms.spell_upper = parms.opt_flip and 'out' or 'in' end if parms.opt_table or parms.opt_tablecen then if abbr_entered == nil and parms.lk == nil then parms.opt_values = true end parms.table_align = parms.opt_table and 'right' or 'center' end if parms.table_align or parms.opt_sortable_on then parms.need_table_or_sort = true end local disp_joins = text_code.disp_joins local default_joins = disp_joins['b'] parms.join_between = default_joins[3] or '; ' local disp = parms.disp if disp == nil then -- special case for the most common setting parms.joins = default_joins elseif disp == 'x' then -- Later, parms.joins is set from the input parameters. else -- Old template does this. local abbr = parms.abbr if disp == 'slash' then if abbr_entered == nil then disp = 'slash-nbsp' elseif abbr == 'in' or abbr == 'out' then disp = 'slash-sp' else disp = 'slash-nosp' end elseif disp == 'sqbr' then if abbr == 'on' then disp = 'sqbr-nbsp' else disp = 'sqbr-sp' end end parms.joins = disp_joins[disp] or default_joins parms.join_between = parms.joins[3] or parms.join_between parms.wantname = parms.joins.wantname end if (en_default and not parms.opt_lang_local and (parms[1] or ''):find('%d')) or parms.opt_lang_en then from_en_table = nil end if en_default and from_en_table then -- For hiwiki: localized symbol/name is defined with the US symbol/name field, -- and is used if output uses localized numbers. parms.opt_sp_us = true end return true end local function get_values(parms) -- If successful, update parms and return true, v, i where -- v = table of input values -- i = index to next entry in parms after those processed here -- or return false, t where t is an error message table. local valinfo = collection() -- numbered table of input values local range = collection() -- numbered table of range items (having, for example, 2 range items requires 3 input values) local had_nocomma -- true if removed "nocomma" kludge from second parameter (like "tonocomma") local parm2 = strip(parms[2]) if parm2 and parm2:sub(-7, -1) == 'nocomma' then parms[2] = strip(parm2:sub(1, -8)) parms.opt_nocomma = true had_nocomma = true end local function extractor(i) -- If the parameter is not a value, try unpacking it as a range ("1-23" for "1 to 23"). -- However, "-1-2/3" is a negative fraction (-1⅔), so it must be extracted first. -- Do not unpack a parameter if it is like "3-1/2" which is sometimes incorrectly -- used instead of "3+1/2" (and which should not be interpreted as "3 to ½"). -- Unpacked items are inserted into the parms table. -- The tail recursion allows combinations like "1x2 to 3x4". local valstr = strip(parms[i]) -- trim so any '-' as a negative sign will be at start local success, result = extract_number(parms, valstr, i > 1) if not success and valstr and i < 20 then -- check i to limit abuse local lhs, sep, rhs = valstr:match('^(%S+)%s+(%S+)%s+(%S.*)') if lhs and not (sep == '-' and rhs:match('/')) then if sep:find('%d') then return success, result -- to reject {{convert|1 234 567|m}} with a decent message (en only) end parms[i] = rhs table.insert(parms, i, sep) table.insert(parms, i, lhs) return extractor(i) end if not valstr:match('%-.*/') then for _, sep in ipairs(text_code.ranges.words) do local start, stop = valstr:find(sep, 2, true) -- start at 2 to skip any negative sign for range '-' if start then parms[i] = valstr:sub(stop + 1) table.insert(parms, i, sep) table.insert(parms, i, valstr:sub(1, start - 1)) return extractor(i) end end end end return success, result end local i = 1 local is_change while true do local success, info = extractor(i) -- need to set parms.opt_nocomma before calling this if not success then return false, info end i = i + 1 if is_change then info.is_change = true -- value is after "±" and so is a change (significant for range like {{convert|5|±|5|°C}}) is_change = nil end valinfo:add(info) local range_item = get_range(strip(parms[i])) if not range_item then break end i = i + 1 range:add(range_item) if type(range_item) == 'table' then -- For range "x", if append unit to some values, append it to all. parms.in_range_x = parms.in_range_x or range_item.in_range_x parms.out_range_x = parms.out_range_x or range_item.out_range_x parms.abbr_range_x = parms.abbr_range_x or range_item.abbr_range_x is_change = range_item.is_range_change end end if range.n > 0 then if range.n > 30 then -- limit abuse, although 4 is a more likely upper limit return false, { 'cvt_invalid_num' } -- misleading message but it will do end parms.range = range elseif had_nocomma then return false, { 'cvt_unknown', parm2 } end return true, valinfo, i end local function simple_get_values(parms) -- If input is like "{{convert|valid_value|valid_unit|...}}", -- return true, i, in_unit, in_unit_table -- i = index in parms of what follows valid_unit, if anything. -- The valid_value is not negative and does not use a fraction, and -- no options requiring further processing of the input are used. -- Otherwise, return nothing or return false, parm1 for caller to interpret. -- Testing shows this function is successful for 96% of converts in articles, -- and that on average it speeds up converts by 8%. local clean = to_en(strip(parms[1] or ''), parms) if parms.opt_ri or parms.opt_spell_in or #clean > 10 or not clean:match('^[0-9.]+$') then return false, clean end local value = tonumber(clean) if not value then return end local info = { value = value, altvalue = value, singular = (value == 1), clean = clean, show = with_separator(parms, clean), } local in_unit = strip(parms[2]) local success, in_unit_table = lookup(parms, in_unit, 'no_combination') if not success then return end in_unit_table.valinfo = { info } return true, 3, in_unit, in_unit_table end local function wikidata_call(parms, operation, ...) -- Return true, s where s is the result of a Wikidata operation, -- or return false, t where t is an error message table. local function worker(...) wikidata_code = wikidata_code or require(wikidata_module) wikidata_data = wikidata_data or mw.loadData(wikidata_data_module) return wikidata_code[operation](wikidata_data, ...) end local success, status, result = pcall(worker, ...) if success then return status, result end if parms.opt_sortable_debug then -- Use debug=yes to crash if an error while accessing Wikidata. error('Error accessing Wikidata: ' .. status, 0) end return false, { 'cvt_wd_fail' } end local function get_parms(parms, args) -- If successful, update parms and return true, unit where -- parms is a table of all arguments passed to the template -- converted to named arguments, and -- unit is the input unit table; -- or return false, t where t is an error message table. -- For special processing (not a convert), can also return -- true, wikitext where wikitext is the final result. -- The returned input unit table may be for a fake unit using the specified -- unit code as the symbol and name, and with bad_mcode = message code table. -- MediaWiki removes leading and trailing whitespace from the values of -- named arguments. However, the values of numbered arguments include any -- whitespace entered in the template, and whitespace is used by some -- parameters (example: the numbered parameters associated with "disp=x"). local kv_pairs = {} -- table of input key:value pairs where key is a name; needed because cannot iterate parms and add new fields to it for k, v in pairs(args) do if type(k) == 'number' or k == 'test' then -- parameter "test" is reserved for testing and is not translated parms[k] = v else kv_pairs[k] = v end end if parms.test == 'wikidata' then local ulookup = function (ucode) -- Use empty table for parms so it does not accumulate results when used repeatedly. return lookup({}, ucode, 'no_combination') end return wikidata_call(parms, '_listunits', ulookup) end local success, msg = translate_parms(parms, kv_pairs) if not success then return false, msg end if parms.input then success, msg = wikidata_call(parms, '_adjustparameters', parms, 1) if not success then return false, msg end end local success, i, in_unit, in_unit_table = simple_get_values(parms) if not success then if type(i) == 'string' and i:match('^NNN+$') then -- Some infoboxes have examples like {{convert|NNN|m}} (3 or more "N"). -- Output an empty string for these. return false, { 'cvt_no_output' } end local valinfo success, valinfo, i = get_values(parms) if not success then return false, valinfo end in_unit = strip(parms[i]) i = i + 1 success, in_unit_table = lookup(parms, in_unit, 'no_combination') if not success then in_unit = in_unit or '' if parms.opt_ignore_error then -- display given unit code with no error (for use with {{val}}) in_unit_table = '' -- suppress error message and prevent processing of output unit end in_unit_table = setmetatable({ symbol = in_unit, name2 = in_unit, utype = in_unit, scale = 1, default = '', defkey = '', linkey = '', bad_mcode = in_unit_table }, unit_mt) end in_unit_table.valinfo = valinfo end if parms.test == 'msg' then -- Am testing the messages produced when no output unit is specified, and -- the input unit has a missing or invalid default. -- Set two units for testing that. -- LATER: Remove this code. if in_unit == 'chain' then in_unit_table.default = nil -- no default elseif in_unit == 'rd' then in_unit_table.default = "ft!X!m" -- an invalid expression end end in_unit_table.inout = 'in' -- this is an input unit if not parms.range then local success, inext, composite_unit = get_composite(parms, i, in_unit_table) if not success then return false, inext end if composite_unit then in_unit_table = composite_unit i = inext end end if in_unit_table.builtin == 'mach' then -- As with old template, a number following Mach as the input unit is the altitude. -- That is deprecated: should use altitude_ft=NUMBER or altitude_m=NUMBER. local success, info success = tonumber(parms[i]) -- this will often work and will give correct result for values like 2e4 without forcing output scientific notation if success then info = { value = success } else success, info = extract_number(parms, parms[i], false, true) end if success then i = i + 1 in_unit_table.altitude = info.value end end local word = strip(parms[i]) i = i + 1 local precision, is_bad_precision local function set_precision(text) local number, is_integer = get_number(text) if number then if is_integer then precision = number else precision = text is_bad_precision = true end return true -- text was used for precision, good or bad end end if word and not set_precision(word) then parms.out_unit = parms.out_unit or word if set_precision(strip(parms[i])) then i = i + 1 end end if parms.opt_adj_mid then word = parms[i] i = i + 1 if word then -- mid-text words if word:sub(1, 1) == '-' then parms.mid = word else parms.mid = ' ' .. word end end end if parms.opt_one_preunit then parms[parms.opt_flip and 'preunit2' or 'preunit1'] = preunits(1, parms[i]) i = i + 1 end if parms.disp == 'x' then -- Following is reasonably compatible with the old template. local first = parms[i] or '' local second = parms[i+1] or '' i = i + 2 if strip(first) == '' then -- user can enter '&#32;' rather than ' ' to avoid the default first = ' [&nbsp;' .. first second = '&nbsp;]' .. second end parms.joins = { first, second } elseif parms.opt_two_preunits then local p1, p2 = preunits(2, parms[i], parms[i+1]) i = i + 2 if parms.preunit1 then -- To simplify documentation, allow unlikely use of adj=pre with disp=preunit -- (however, an output unit must be specified with adj=pre and with disp=preunit). parms.preunit1 = parms.preunit1 .. p1 parms.preunit2 = p2 else parms.preunit1, parms.preunit2 = p1, p2 end end if precision == nil then if set_precision(strip(parms[i])) then i = i + 1 end end if is_bad_precision then add_warning(parms, 1, 'cvt_bad_prec', precision) else parms.precision = precision end for j = i, i + 3 do local parm = parms[j] -- warn if find a non-empty extraneous parameter if parm and parm:match('%S') then add_warning(parms, 1, 'cvt_unknown_option', parm) break end end return true, in_unit_table end local function record_default_precision(parms, out_current, precision) -- If necessary, adjust parameters and return a possibly adjusted precision. -- When converting a range of values where a default precision is required, -- that default is calculated for each value because the result sometimes -- depends on the precise input and output values. This function may cause -- the entire convert process to be repeated in order to ensure that the -- same default precision is used for each individual convert. -- If that were not done, a range like 1000 to 1000.4 may give poor results -- because the first output could be heavily rounded, while the second is not. -- For range 1000.4 to 1000, this function can give the second convert the -- same default precision that was used for the first. if not parms.opt_round_each then local maxdef = out_current.max_default_precision if maxdef then if maxdef < precision then parms.do_convert_again = true out_current.max_default_precision = precision else precision = out_current.max_default_precision end else out_current.max_default_precision = precision end end return precision end local function default_precision(parms, invalue, inclean, denominator, outvalue, in_current, out_current, extra) -- Return a default value for precision (an integer like 2, 0, -2). -- If denominator is not nil, it is the value of the denominator in inclean. -- Code follows procedures used in old template. local fudge = 1e-14 -- {{Order of magnitude}} adds this, so we do too local prec, minprec, adjust local subunit_ignore_trailing_zero local subunit_more_precision -- kludge for "in" used in input like "|2|ft|6|in" local composite = in_current.composite if composite then subunit_ignore_trailing_zero = true -- input "|2|st|10|lb" has precision 0, not -1 if composite[#composite].exception == 'subunit_more_precision' then subunit_more_precision = true -- do not use standard precision with input like "|2|ft|6|in" end end if denominator and denominator > 0 then prec = math.max(log10(denominator), 1) else -- Count digits after decimal mark, handling cases like '12.345e6'. local exponent local integer, dot, decimals, expstr = inclean:match('^(%d*)(%.?)(%d*)(.*)') local e = expstr:sub(1, 1) if e == 'e' or e == 'E' then exponent = tonumber(expstr:sub(2)) end if dot == '' then prec = subunit_ignore_trailing_zero and 0 or -integer:match('0*$'):len() else prec = #decimals end if exponent then -- So '1230' and '1.23e3' both give prec = -1, and '0.00123' and '1.23e-3' give 5. prec = prec - exponent end end if in_current.istemperature and out_current.istemperature then -- Converting between common temperatures (°C, °F, °R, K); not keVT. -- Kelvin value can be almost zero, or small but negative due to precision problems. -- Also, an input value like -300 C (below absolute zero) gives negative kelvins. -- Calculate minimum precision from absolute value. adjust = 0 local kelvin = abs((invalue - in_current.offset) * in_current.scale) if kelvin < 1e-8 then -- assume nonzero due to input or calculation precision problem minprec = 2 else minprec = 2 - floor(log10(kelvin) + fudge) -- 3 sigfigs in kelvin end else if invalue == 0 or outvalue <= 0 then -- We are never called with a negative outvalue, but it might be zero. -- This is special-cased to avoid calculation exceptions. return record_default_precision(parms, out_current, 0) end if out_current.exception == 'integer_more_precision' and floor(invalue) == invalue then -- With certain output units that sometimes give poor results -- with default rounding, use more precision when the input -- value is equal to an integer. An example of a poor result -- is when input 50 gives a smaller output than input 49.5. -- Experiment shows this helps, but it does not eliminate all -- surprises because it is not clear whether "50" should be -- interpreted as "from 45 to 55" or "from 49.5 to 50.5". adjust = -log10(in_current.scale) elseif subunit_more_precision then -- Conversion like "{{convert|6|ft|1|in|cm}}" (where subunit is "in") -- has a non-standard adjust value, to give more output precision. adjust = log10(out_current.scale) + 2 else adjust = log10(abs(invalue / outvalue)) end adjust = adjust + log10(2) -- Ensure that the output has at least two significant figures. minprec = 1 - floor(log10(outvalue) + fudge) end if extra then adjust = extra.adjust or adjust minprec = extra.minprec or minprec end return record_default_precision(parms, out_current, math.max(floor(prec + adjust), minprec)) end local function convert(parms, invalue, info, in_current, out_current) -- Convert given input value from one unit to another. -- Return output_value (a number) if a simple convert, or -- return f, t where -- f = true, t = table of information with results, or -- f = false, t = error message table. local inscale = in_current.scale local outscale = out_current.scale if not in_current.iscomplex and not out_current.iscomplex then return invalue * (inscale / outscale) -- minimize overhead for most common case end if in_current.invert or out_current.invert then -- Inverted units, such as inverse length, inverse time, or -- fuel efficiency. Built-in units do not have invert set. if (in_current.invert or 1) * (out_current.invert or 1) < 0 then return 1 / (invalue * inscale * outscale) end return invalue * (inscale / outscale) elseif in_current.offset then -- Temperature (there are no built-ins for this type of unit). if info.is_change then return invalue * (inscale / outscale) end return (invalue - in_current.offset) * (inscale / outscale) + out_current.offset else -- Built-in unit. local in_builtin = in_current.builtin local out_builtin = out_current.builtin if in_builtin and out_builtin then if in_builtin == out_builtin then return invalue end -- There are no cases (yet) where need to convert from one -- built-in unit to another, so this should never occur. return false, { 'cvt_bug_convert' } end if in_builtin == 'mach' or out_builtin == 'mach' then -- Should check that only one altitude is given but am planning to remove -- in_current.altitude (which can only occur when Mach is the input unit), -- and out_current.altitude cannot occur. local alt = parms.altitude_ft or in_current.altitude if not alt and parms.altitude_m then alt = parms.altitude_m / 0.3048 -- 1 ft = 0.3048 m end local spd = speed_of_sound(alt) if in_builtin == 'mach' then inscale = spd return invalue * (inscale / outscale) end outscale = spd local adjust = 0.1 / inscale return true, { outvalue = invalue * (inscale / outscale), adjust = log10(adjust) + log10(2), } elseif in_builtin == 'hand' then -- 1 hand = 4 inches; 1.2 hands = 6 inches. -- Decimals of a hand are only defined for the first digit, and -- the first fractional digit should be a number of inches (1, 2 or 3). -- However, this code interprets the entire fractional part as the number -- of inches / 10 (so 1.75 inches would be 0.175 hands). -- A value like 12.3 hands is exactly 12*4 + 3 inches; base default precision on that. local integer, fracpart = math.modf(invalue) local inch_value = 4 * integer + 10 * fracpart -- equivalent number of inches local factor = inscale / outscale if factor == 4 then -- Am converting to inches: show exact result, and use "inches" not "in" by default. if parms.abbr_org == nil then out_current.usename = true end local show = format('%g', abs(inch_value)) -- show and clean are unsigned if not show:find('e', 1, true) then return true, { invalue = inch_value, outvalue = inch_value, clean = show, show = show, } end end local outvalue = (integer + 2.5 * fracpart) * factor local fracstr = info.clean:match('%.(.*)') or '' local fmt if fracstr == '' then fmt = '%.0f' else fmt = '%.' .. format('%d', #fracstr - 1) .. 'f' end return true, { invalue = inch_value, clean = format(fmt, inch_value), outvalue = outvalue, minprec = 0, } end end return false, { 'cvt_bug_convert' } -- should never occur end local function user_style(parms, i) -- Return text for a user-specified style for a table cell, or '' if none, -- given i = 1 (input style) or 2 (output style). local style = parms[(i == 1) and 'stylein' or 'styleout'] if style then style = style:gsub('"', '') if style ~= '' then if style:sub(-1) ~= ';' then style = style .. ';' end return style end end return '' end local function make_table_or_sort(parms, invalue, info, in_current, scaled_top) -- Set options to handle output for a table or a sort key, or both. -- The text sort key is based on the value resulting from converting -- the input to a fake base unit with scale = 1, and other properties -- required for a conversion derived from the input unit. -- For other modules, return the sort key in a hidden span element, and -- the scaled value used to generate the sort key. -- If scaled_top is set, it is the scaled value of the numerator of a per unit -- to be combined with this unit (the denominator) to make the sort key. -- Scaling only works with units that convert with a factor (not temperature). local sortkey, scaled_value if parms.opt_sortable_on then local base = { -- a fake unit with enough fields for a valid convert scale = 1, invert = in_current.invert and 1, iscomplex = in_current.iscomplex, offset = in_current.offset and 0, } local outvalue, extra = convert(parms, invalue, info, in_current, base) if extra then outvalue = extra.outvalue end if in_current.istemperature then -- Have converted to kelvin; assume numbers close to zero have a -- rounding error and should be zero. if abs(outvalue) < 1e-12 then outvalue = 0 end end if scaled_top and outvalue ~= 0 then outvalue = scaled_top / outvalue end scaled_value = outvalue if not valid_number(outvalue) then if outvalue < 0 then sortkey = '1000000000000000000' else sortkey = '9000000000000000000' end elseif outvalue == 0 then sortkey = '5000000000000000000' else local mag = floor(log10(abs(outvalue)) + 1e-14) local prefix if outvalue > 0 then prefix = 7000 + mag else prefix = 2999 - mag outvalue = outvalue + 10^(mag+1) end sortkey = format('%d', prefix) .. format('%015.0f', floor(outvalue * 10^(14-mag))) end end local sortspan if sortkey and not parms.table_align then sortspan = parms.opt_sortable_debug and '<span data-sort-value="' .. sortkey .. '♠"><span style="border:1px solid">' .. sortkey .. '♠</span></span>' or '<span data-sort-value="' .. sortkey .. '♠"></span>' parms.join_before = sortspan end if parms.table_align then local sort if sortkey then sort = ' data-sort-value="' .. sortkey .. '"' if parms.opt_sortable_debug then parms.join_before = '<span style="border:1px solid">' .. sortkey .. '</span>' end else sort = '' end local style = 'style="text-align:' .. parms.table_align .. ';' local joins = {} for i = 1, 2 do joins[i] = (i == 1 and '' or '\n|') .. style .. user_style(parms, i) .. '"' .. sort .. '|' end parms.table_joins = joins end return sortspan, scaled_value end local cvt_to_hand local function cvtround(parms, info, in_current, out_current) -- Return true, t where t is a table with the conversion results; fields: -- show = rounded, formatted string with the result of converting value in info, -- using the rounding specified in parms. -- singular = true if result (after rounding and ignoring any negative sign) -- is "1", or like "1.00", or is a fraction with value < 1; -- (and more fields shown below, and a calculated 'absvalue' field). -- or return false, t where t is an error message table. -- Input info.clean uses en digits (it has been translated, if necessary). -- Output show uses en or non-en digits as appropriate, or can be spelled. if out_current.builtin == 'hand' then return cvt_to_hand(parms, info, in_current, out_current) end local invalue = in_current.builtin == 'hand' and info.altvalue or info.value local outvalue, extra = convert(parms, invalue, info, in_current, out_current) if parms.need_table_or_sort then parms.need_table_or_sort = nil -- process using first input value only make_table_or_sort(parms, invalue, info, in_current) end if extra then if not outvalue then return false, extra end invalue = extra.invalue or invalue outvalue = extra.outvalue end if not valid_number(outvalue) then return false, { 'cvt_invalid_num' } end local isnegative if outvalue < 0 then isnegative = true outvalue = -outvalue end local precision, show, exponent local denominator = out_current.frac if denominator then show = fraction_table(outvalue, denominator) else precision = parms.precision if not precision then if parms.sigfig then show, exponent = make_sigfig(outvalue, parms.sigfig) elseif parms.opt_round then local n = parms.opt_round if n == 0.5 then local integer, fracpart = math.modf(floor(2 * outvalue + 0.5) / 2) if fracpart == 0 then show = format('%.0f', integer) else show = format('%.1f', integer + fracpart) end else show = format('%.0f', floor((outvalue / n) + 0.5) * n) end elseif in_current.builtin == 'mach' then local sigfig = info.clean:gsub('^[0.]+', ''):gsub('%.', ''):len() + 1 show, exponent = make_sigfig(outvalue, sigfig) else local inclean = info.clean if extra then inclean = extra.clean or inclean show = extra.show end if not show then precision = default_precision(parms, invalue, inclean, info.denominator, outvalue, in_current, out_current, extra) end end end end if precision then if precision >= 0 then local fudge if precision <= 8 then -- Add a fudge to handle common cases of bad rounding due to inability -- to precisely represent some values. This makes the following work: -- {{convert|-100.1|C|K}} and {{convert|5555000|um|m|2}}. -- Old template uses #expr round, which invokes PHP round(). -- LATER: Investigate how PHP round() works. fudge = 2e-14 else fudge = 0 end local fmt = '%.' .. format('%d', precision) .. 'f' local success success, show = pcall(format, fmt, outvalue + fudge) if not success then return false, { 'cvt_big_prec', tostring(precision) } end else precision = -precision -- #digits to zero (in addition to any digits after dot) local shift = 10 ^ precision show = format('%.0f', outvalue/shift) if show ~= '0' then exponent = #show + precision end end end local t = format_number(parms, show, exponent, isnegative) if type(show) == 'string' then -- Set singular using match because on some systems 0.99999999999999999 is 1.0. if exponent then t.singular = (exponent == 1 and show:match('^10*$')) else t.singular = (show == '1' or show:match('^1%.0*$')) end else t.fraction_table = show t.singular = (outvalue <= 1) -- cannot have 'fraction == 1', but if it were possible it would be singular end t.raw_absvalue = outvalue -- absolute value before rounding return true, setmetatable(t, { __index = function (self, key) if key == 'absvalue' then -- Calculate absolute value after rounding, if needed. local clean, exponent = rawget(self, 'clean'), rawget(self, 'exponent') local value = tonumber(clean) -- absolute value (any negative sign has been ignored) if exponent then value = value * 10^exponent end rawset(self, key, value) return value end end }) end function cvt_to_hand(parms, info, in_current, out_current) -- Convert input to hands, inches. -- Return true, t where t is a table with the conversion results; -- or return false, t where t is an error message table. if parms.abbr_org == nil then out_current.usename = true -- default is to show name not symbol end local precision = parms.precision local frac = out_current.frac if not frac and precision and precision > 1 then frac = (precision == 2) and 2 or 4 end local out_next = out_current.out_next if out_next then -- Use magic knowledge to determine whether the next unit is inches without requiring i18n. -- The following ensures that when the output combination "hand in" is used, the inches -- value is rounded to match the hands value. Also, displaying say "61½" instead of 61.5 -- is better as 61.5 implies the value is not 61.4. if out_next.exception == 'subunit_more_precision' then out_next.frac = frac end end -- Convert to inches; calculate hands from that. local dummy_unit_table = { scale = out_current.scale / 4, frac = frac } local success, outinfo = cvtround(parms, info, in_current, dummy_unit_table) if not success then return false, outinfo end local tfrac = outinfo.fraction_table local inches = outinfo.raw_absvalue if tfrac then inches = floor(inches) -- integer part only; fraction added later else inches = floor(inches + 0.5) -- a hands measurement never shows decimals of an inch end local hands, inches = divide(inches, 4) outinfo.absvalue = hands + inches/4 -- supposed to be the absolute rounded value, but this is close enough local inchstr = tostring(inches) -- '0', '1', '2' or '3' if precision and precision <= 0 then -- using negative or 0 for precision rounds to nearest hand hands = floor(outinfo.raw_absvalue/4 + 0.5) inchstr = '' elseif tfrac then -- Always show an integer before fraction (like "15.0½") because "15½" means 15-and-a-half hands. inchstr = numdot .. format_fraction(parms, 'out', false, inchstr, tfrac.numstr, tfrac.denstr) else inchstr = numdot .. from_en(inchstr) end outinfo.show = outinfo.sign .. with_separator(parms, format('%.0f', hands)) .. inchstr return true, outinfo end local function evaluate_condition(value, condition) -- Return true or false from applying a conditional expression to value, -- or throw an error if invalid. -- A very limited set of expressions is supported: -- v < 9 -- v * 9 < 9 -- where -- 'v' is replaced with value -- 9 is any number (as defined by Lua tonumber) -- only en digits are accepted -- '<' can also be '<=' or '>' or '>=' -- In addition, the following form is supported: -- LHS and RHS -- where -- LHS, RHS = any of above expressions. local function compare(value, text) local arithop, factor, compop, limit = text:match('^%s*v%s*([*]?)(.-)([<>]=?)(.*)$') if arithop == nil then error('Invalid default expression', 0) elseif arithop == '*' then factor = tonumber(factor) if factor == nil then error('Invalid default expression', 0) end value = value * factor end limit = tonumber(limit) if limit == nil then error('Invalid default expression', 0) end if compop == '<' then return value < limit elseif compop == '<=' then return value <= limit elseif compop == '>' then return value > limit elseif compop == '>=' then return value >= limit end error('Invalid default expression', 0) -- should not occur end local lhs, rhs = condition:match('^(.-%W)and(%W.*)') if lhs == nil then return compare(value, condition) end return compare(value, lhs) and compare(value, rhs) end local function get_default(value, unit_table) -- Return true, s where s = name of unit's default output unit, -- or return false, t where t is an error message table. -- Some units have a default that depends on the input value -- (the first value if a range of values is used). -- If '!' is in the default, the first bang-delimited field is an -- expression that uses 'v' to represent the input value. -- Example: 'v < 120 ! small ! big ! suffix' (suffix is optional) -- evaluates 'v < 120' as a boolean with result -- 'smallsuffix' if (value < 120), or 'bigsuffix' otherwise. -- Input must use en digits and '.' decimal mark. local default = data_code.default_exceptions[unit_table.defkey or unit_table.symbol] or unit_table.default if not default then local per = unit_table.per if per then local function a_default(v, u) local success, ucode = get_default(v, u) if not success then return '?' -- an unlikely error has occurred; will cause lookup of default to fail end -- Attempt to use only the first unit if a combination or output multiple. -- This is not bulletproof but should work for most cases. -- Where it does not work, the convert will need to specify the wanted output unit. local t = all_units[ucode] if t then local combo = t.combination if combo then -- For a multiple like ftin, the "first" unit (ft) is last in the combination. local i = t.multiple and table_len(combo) or 1 ucode = combo[i] end else -- Try for an automatically generated combination. local item = ucode:match('^(.-)%+') or ucode:match('^(%S+)%s') if all_units[item] then return item end end return ucode end local unit1, unit2 = per[1], per[2] local def1 = (unit1 and a_default(value, unit1) or unit_table.vprefix or '') local def2 = a_default(1, unit2) -- 1 because per unit of denominator return true, def1 .. '/' .. def2 end return false, { 'cvt_no_default', unit_table.symbol } end if default:find('!', 1, true) == nil then return true, default end local t = split(default, '!') if #t == 3 or #t == 4 then local success, result = pcall(evaluate_condition, value, t[1]) if success then default = result and t[2] or t[3] if #t == 4 then default = default .. t[4] end return true, default end end return false, { 'cvt_bad_default', unit_table.symbol } end local linked_pages -- to record linked pages so will not link to the same page more than once local function unlink(unit_table) -- Forget that the given unit has previously been linked (if it has). -- That is needed when processing a range of inputs or outputs when an id -- for the first range value may have been evaluated, but only an id for -- the last value is displayed, and that id may need to be linked. linked_pages[unit_table.unitcode or unit_table] = nil end local function make_link(link, id, unit_table) -- Return wikilink "[[link|id]]", possibly abbreviated as in examples: -- [[Mile|mile]] --> [[mile]] -- [[Mile|miles]] --> [[mile]]s -- However, just id is returned if: -- * no link given (so caller does not need to check if a link was defined); or -- * link has previously been used during the current convert (to avoid overlinking). local link_key if unit_table then link_key = unit_table.unitcode or unit_table else link_key = link end if not link or link == '' or linked_pages[link_key] then return id end linked_pages[link_key] = true -- Following only works for language en, but it should be safe on other wikis, -- and overhead of doing it generally does not seem worthwhile. local l = link:sub(1, 1):lower() .. link:sub(2) if link == id or l == id then return '[[' .. id .. ']]' elseif link .. 's' == id or l .. 's' == id then return '[[' .. id:sub(1, -2) .. ']]s' else return '[[' .. link .. '|' .. id .. ']]' end end local function variable_name(clean, unit_table) -- For slwiki, a unit name depends on the value. -- Parameter clean is the unsigned rounded value in en digits, as a string. -- Value Source Example for "m" -- integer 1: name1 meter (also is the name of the unit) -- integer 2: var{1} metra -- integer 3 and 4: var{2} metri -- integer else: var{3} metrov (0 and 5 or more) -- real/fraction: var{4} metra -- var{i} means the i'th field in unit_table.varname if it exists and has -- an i'th field, otherwise name2. -- Fields are separated with "!" and are not empty. -- A field for a unit using an SI prefix has the prefix name inserted, -- replacing '#' if found, or before the field otherwise. local vname if clean == '1' then vname = unit_table.name1 elseif unit_table.varname then local i if clean == '2' then i = 1 elseif clean == '3' or clean == '4' then i = 2 elseif clean:find('.', 1, true) then i = 4 else i = 3 end if i > 1 and varname == 'pl' then i = i - 1 end vname = split(unit_table.varname, '!')[i] end if vname then local si_name = rawget(unit_table, 'si_name') or '' local pos = vname:find('#', 1, true) if pos then vname = vname:sub(1, pos - 1) .. si_name .. vname:sub(pos + 1) else vname = si_name .. vname end return vname end return unit_table.name2 end local function linked_id(parms, unit_table, key_id, want_link, clean) -- Return final unit id (symbol or name), optionally with a wikilink, -- and update unit_table.sep if required. -- key_id is one of: 'symbol', 'sym_us', 'name1', 'name1_us', 'name2', 'name2_us'. local abbr_on = (key_id == 'symbol' or key_id == 'sym_us') if abbr_on and want_link then local symlink = rawget(unit_table, 'symlink') if symlink then return symlink -- for exceptions that have the linked symbol built-in end end local multiplier = rawget(unit_table, 'multiplier') local per = unit_table.per if per then local paren1, paren2 = '', '' -- possible parentheses around bottom unit local unit1 = per[1] -- top unit_table, or nil local unit2 = per[2] -- bottom unit_table if abbr_on then if not unit1 then unit_table.sep = '' -- no separator in "$2/acre" end if not want_link then local symbol = unit_table.symbol_raw if symbol then return symbol -- for exceptions that have the symbol built-in end end if (unit2.symbol):find('⋅', 1, true) then paren1, paren2 = '(', ')' end end local key_id2 -- unit2 is always singular if key_id == 'name2' then key_id2 = 'name1' elseif key_id == 'name2_us' then key_id2 = 'name1_us' else key_id2 = key_id end local result if abbr_on then result = '/' elseif omitsep then result = per_word elseif unit1 then result = ' ' .. per_word .. ' ' else result = per_word .. ' ' end if want_link and unit_table.link then if abbr_on or not varname then result = (unit1 and linked_id(parms, unit1, key_id, false, clean) or '') .. result .. linked_id(parms, unit2, key_id2, false, '1') else result = (unit1 and variable_name(clean, unit1) or '') .. result .. variable_name('1', unit2) end if omit_separator(result) then unit_table.sep = '' end return make_link(unit_table.link, result, unit_table) end if unit1 then result = linked_id(parms, unit1, key_id, want_link, clean) .. result if unit1.sep then unit_table.sep = unit1.sep end elseif omitsep then unit_table.sep = '' end return result .. paren1 .. linked_id(parms, unit2, key_id2, want_link, '1') .. paren2 end if multiplier then -- A multiplier (like "100" in "100km") forces the unit to be plural. multiplier = from_en(multiplier) if not omitsep then multiplier = multiplier .. (abbr_on and '&nbsp;' or ' ') end if not abbr_on then if key_id == 'name1' then key_id = 'name2' elseif key_id == 'name1_us' then key_id = 'name2_us' end end else multiplier = '' end local id = unit_table.fixed_name or ((varname and not abbr_on) and variable_name(clean, unit_table) or unit_table[key_id]) if omit_separator(id) then unit_table.sep = '' end if want_link then local link = data_code.link_exceptions[unit_table.linkey or unit_table.symbol] or unit_table.link if link then local before = '' local i = unit_table.customary if i == 1 and parms.opt_sp_us then i = 2 -- show "U.S." not "US" end if i == 3 and abbr_on then i = 4 -- abbreviate "imperial" to "imp" end local customary = text_code.customary_units[i] if customary then -- LATER: This works for language en only, but it's esoteric so ignore for now. local pertext if id:sub(1, 1) == '/' then -- Want unit "/USgal" to display as "/U.S. gal", not "U.S. /gal". pertext = '/' id = id:sub(2) elseif id:sub(1, 4) == 'per ' then -- Similarly want "per U.S. gallon", not "U.S. per gallon" (but in practice this is unlikely to be used). pertext = 'per ' id = id:sub(5) else pertext = '' end -- Omit any "US"/"U.S."/"imp"/"imperial" from start of id since that will be inserted. local removes = (i < 3) and { 'US&nbsp;', 'US ', 'U.S.&nbsp;', 'U.S. ' } or { 'imp&nbsp;', 'imp ', 'imperial ' } for _, prefix in ipairs(removes) do local plen = #prefix if id:sub(1, plen) == prefix then id = id:sub(plen + 1) break end end before = pertext .. make_link(customary.link, customary[1]) .. ' ' end id = before .. make_link(link, id, unit_table) end end return multiplier .. id end local function make_id(parms, which, unit_table) -- Return id, f where -- id = unit name or symbol, possibly modified -- f = true if id is a name, or false if id is a symbol -- using the value for index 'which', and for 'in' or 'out' (unit_table.inout). -- Result is '' if no symbol/name is to be used. -- In addition, set unit_table.sep = ' ' or '&nbsp;' or '' -- (the separator that caller will normally insert before the id). if parms.opt_values then unit_table.sep = '' return '' end local inout = unit_table.inout local info = unit_table.valinfo[which] local abbr_org = parms.abbr_org local adjectival = parms.opt_adjectival local lk = parms.lk local want_link = (lk == 'on' or lk == inout) local usename = unit_table.usename local singular = info.singular local want_name if usename then want_name = true else if abbr_org == nil then if parms.wantname then want_name = true end if unit_table.usesymbol then want_name = false end end if want_name == nil then local abbr = parms.abbr if abbr == 'on' or abbr == inout or (abbr == 'mos' and inout == 'out') then want_name = false else want_name = true end end end local key if want_name then if lk == nil and unit_table.builtin == 'hand' then want_link = true end if parms.opt_use_nbsp then unit_table.sep = '&nbsp;' else unit_table.sep = ' ' end if parms.opt_singular then local value if inout == 'in' then value = info.value else value = info.absvalue end if value then -- some unusual units do not always set value field value = abs(value) singular = (0 < value and value < 1.0001) end end if unit_table.engscale then -- engscale: so "|1|e3kg" gives "1 thousand kilograms" (plural) singular = false end key = (adjectival or singular) and 'name1' or 'name2' if parms.opt_sp_us then key = key .. '_us' end else if unit_table.builtin == 'hand' then if parms.opt_hand_hh then unit_table.symbol = 'hh' -- LATER: might want i18n applied to this end end unit_table.sep = '&nbsp;' key = parms.opt_sp_us and 'sym_us' or 'symbol' end return linked_id(parms, unit_table, key, want_link, info.clean), want_name end local function decorate_value(parms, unit_table, which, number_word) -- If needed, update unit_table so values will be shown with extra information. -- For consistency with the old template (but different from fmtpower), -- the style to display powers of 10 includes "display:none" to allow some -- browsers to copy, for example, "10³" as "10^3", rather than as "103". local info local engscale = unit_table.engscale local prefix = unit_table.vprefix if engscale or prefix then info = unit_table.valinfo[which] if info.decorated then return -- do not redecorate if repeating convert end info.decorated = true if engscale then local inout = unit_table.inout local abbr = parms.abbr if (abbr == 'on' or abbr == inout) and not parms.number_word then info.show = info.show .. '<span style="margin-left:0.2em">×<span style="margin-left:0.1em">' .. from_en('10') .. '</span></span><s style="display:none">^</s><sup>' .. from_en(tostring(engscale.exponent)) .. '</sup>' elseif number_word then local number_id local lk = parms.lk if lk == 'on' or lk == inout then number_id = make_link(engscale.link, engscale[1]) else number_id = engscale[1] end -- WP:NUMERAL recommends "&nbsp;" in values like "12 million". info.show = info.show .. (parms.opt_adjectival and '-' or '&nbsp;') .. number_id end end if prefix then info.show = prefix .. info.show end end end local function process_input(parms, in_current) -- Processing required once per conversion. -- Return block of text to represent input (value/unit). if parms.opt_output_only or parms.opt_output_number_only or parms.opt_output_unit_only then parms.joins = { '', '' } return '' end local first_unit local composite = in_current.composite -- nil or table of units if composite then first_unit = composite[1] else first_unit = in_current end local id1, want_name = make_id(parms, 1, first_unit) local sep = first_unit.sep -- separator between value and unit, set by make_id local preunit = parms.preunit1 if preunit then sep = '' -- any separator is included in preunit else preunit = '' end if parms.opt_input_unit_only then parms.joins = { '', '' } if composite then local parts = { id1 } for i, unit in ipairs(composite) do if i > 1 then table.insert(parts, (make_id(parms, 1, unit))) end end id1 = table.concat(parts, ' ') end if want_name and parms.opt_adjectival then return preunit .. hyphenated(id1) end return preunit .. id1 end if parms.opt_also_symbol and not composite and not parms.opt_flip then local join1 = parms.joins[1] if join1 == ' (' or join1 == ' [' then parms.joins = { ' [' .. first_unit[parms.opt_sp_us and 'sym_us' or 'symbol'] .. ']' .. join1 , parms.joins[2] } end end if in_current.builtin == 'mach' and first_unit.sep ~= '' then -- '' means omitsep with non-enwiki name local prefix = id1 .. '&nbsp;' local range = parms.range local valinfo = first_unit.valinfo local result = prefix .. valinfo[1].show if range then -- For simplicity and because more not needed, handle one range item only. local prefix2 = make_id(parms, 2, first_unit) .. '&nbsp;' result = range_text(range[1], want_name, parms, result, prefix2 .. valinfo[2].show, 'in', {spaced=true}) end return preunit .. result end if composite then -- Simplify: assume there is no range, and no decoration. local mid = (not parms.opt_flip) and parms.mid or '' local sep1 = '&nbsp;' local sep2 = ' ' if parms.opt_adjectival and want_name then sep1 = '-' sep2 = '-' end if omitsep and sep == '' then -- Testing the id of the most significant unit should be sufficient. sep1 = '' sep2 = '' end local parts = { first_unit.valinfo[1].show .. sep1 .. id1 } for i, unit in ipairs(composite) do if i > 1 then table.insert(parts, unit.valinfo[1].show .. sep1 .. (make_id(parms, 1, unit))) end end return table.concat(parts, sep2) .. mid end local add_unit = (parms.abbr == 'mos') or parms[parms.opt_flip and 'out_range_x' or 'in_range_x'] or (not want_name and parms.abbr_range_x) local range = parms.range if range and not add_unit then unlink(first_unit) end local id = range and make_id(parms, range.n + 1, first_unit) or id1 local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, 'in') if was_hyphenated then add_unit = false end local result local valinfo = first_unit.valinfo if range then for i = 0, range.n do local number_word if i == range.n then add_unit = false number_word = true end decorate_value(parms, first_unit, i+1, number_word) local show = valinfo[i+1].show if add_unit then show = show .. first_unit.sep .. (i == 0 and id1 or make_id(parms, i+1, first_unit)) end if i == 0 then result = show else result = range_text(range[i], want_name, parms, result, show, 'in') end end else decorate_value(parms, first_unit, 1, true) result = valinfo[1].show end return result .. preunit .. extra end local function process_one_output(parms, out_current) -- Processing required for each output unit. -- Return block of text to represent output (value/unit). local inout = out_current.inout -- normally 'out' but can be 'in' for order=out local id1, want_name = make_id(parms, 1, out_current) local sep = out_current.sep -- set by make_id local preunit = parms.preunit2 if preunit then sep = '' -- any separator is included in preunit else preunit = '' end if parms.opt_output_unit_only then if want_name and parms.opt_adjectival then return preunit .. hyphenated(id1) end return preunit .. id1 end if out_current.builtin == 'mach' and out_current.sep ~= '' then -- '' means omitsep with non-enwiki name local prefix = id1 .. '&nbsp;' local range = parms.range local valinfo = out_current.valinfo local result = prefix .. valinfo[1].show if range then -- For simplicity and because more not needed, handle one range item only. result = range_text(range[1], want_name, parms, result, prefix .. valinfo[2].show, inout, {spaced=true}) end return preunit .. result end local add_unit = (parms[parms.opt_flip and 'in_range_x' or 'out_range_x'] or (not want_name and parms.abbr_range_x)) and not parms.opt_output_number_only local range = parms.range if range and not add_unit then unlink(out_current) end local id = range and make_id(parms, range.n + 1, out_current) or id1 local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, inout) if was_hyphenated then add_unit = false end local result local valinfo = out_current.valinfo if range then for i = 0, range.n do local number_word if i == range.n then add_unit = false number_word = true end decorate_value(parms, out_current, i+1, number_word) local show = valinfo[i+1].show if add_unit then show = show .. out_current.sep .. (i == 0 and id1 or make_id(parms, i+1, out_current)) end if i == 0 then result = show else result = range_text(range[i], want_name, parms, result, show, inout) end end else decorate_value(parms, out_current, 1, true) result = valinfo[1].show end if parms.opt_output_number_only then return result end return result .. preunit .. extra end local function make_output_single(parms, in_unit_table, out_unit_table) -- Return true, item where item = wikitext of the conversion result -- for a single output (which is not a combination or a multiple); -- or return false, t where t is an error message table. if parms.opt_order_out and in_unit_table.unitcode == out_unit_table.unitcode then out_unit_table.valinfo = in_unit_table.valinfo else out_unit_table.valinfo = collection() for _, v in ipairs(in_unit_table.valinfo) do local success, info = cvtround(parms, v, in_unit_table, out_unit_table) if not success then return false, info end out_unit_table.valinfo:add(info) end end return true, process_one_output(parms, out_unit_table) end local function make_output_multiple(parms, in_unit_table, out_unit_table) -- Return true, item where item = wikitext of the conversion result -- for an output which is a multiple (like 'ftin'); -- or return false, t where t is an error message table. local inout = out_unit_table.inout -- normally 'out' but can be 'in' for order=out local multiple = out_unit_table.multiple -- table of scaling factors (will not be nil) local combos = out_unit_table.combination -- table of unit tables (will not be nil) local abbr = parms.abbr local abbr_org = parms.abbr_org local disp = parms.disp local want_name = (abbr_org == nil and (disp == 'or' or disp == 'slash')) or not (abbr == 'on' or abbr == inout or abbr == 'mos') local want_link = (parms.lk == 'on' or parms.lk == inout) local mid = parms.opt_flip and parms.mid or '' local sep1 = '&nbsp;' local sep2 = ' ' if parms.opt_adjectival and want_name then sep1 = '-' sep2 = '-' end local do_spell = parms.opt_spell_out parms.opt_spell_out = nil -- so the call to cvtround does not spell the value local function make_result(info, isfirst) local fmt, outvalue, sign local results = {} for i = 1, #combos do local tfrac, thisvalue, strforce local out_current = combos[i] out_current.inout = inout local scale = multiple[i] if i == 1 then -- least significant unit ('in' from 'ftin') local decimals out_current.frac = out_unit_table.frac local success, outinfo = cvtround(parms, info, in_unit_table, out_current) if not success then return false, outinfo end if isfirst then out_unit_table.valinfo = { outinfo } -- in case output value of first least significant unit is needed end sign = outinfo.sign tfrac = outinfo.fraction_table if outinfo.is_scientific then strforce = outinfo.show decimals = '' elseif tfrac then decimals = '' else local show = outinfo.show -- number as a string in local language local p1, p2 = show:find(numdot, 1, true) decimals = p1 and show:sub(p2 + 1) or '' -- text after numdot, if any end fmt = '%.' .. ulen(decimals) .. 'f' -- to reproduce precision if decimals == '' then if tfrac then outvalue = floor(outinfo.raw_absvalue) -- integer part only; fraction added later else outvalue = floor(outinfo.raw_absvalue + 0.5) -- keep all integer digits of least significant unit end else outvalue = outinfo.absvalue end end if scale then outvalue, thisvalue = divide(outvalue, scale) else thisvalue = outvalue end local id if want_name then if varname then local clean if strforce or tfrac then clean = '.1' -- dummy value to force name for floating point else clean = format(fmt, thisvalue) end id = variable_name(clean, out_current) else local key = 'name2' if parms.opt_adjectival then key = 'name1' elseif tfrac then if thisvalue == 0 then key = 'name1' end elseif parms.opt_singular then if 0 < thisvalue and thisvalue < 1.0001 then key = 'name1' end else if thisvalue == 1 then key = 'name1' end end id = out_current[key] end else id = out_current['symbol'] end if i == 1 and omit_separator(id) then -- Testing the id of the least significant unit should be sufficient. sep1 = '' sep2 = '' end if want_link then local link = out_current.link if link then id = make_link(link, id, out_current) end end local strval local spell_inout = (i == #combos or outvalue == 0) and inout or '' -- trick so the last value processed (first displayed) has uppercase, if requested if strforce and outvalue == 0 then sign = '' -- any sign is in strforce strval = strforce -- show small values in scientific notation; will only use least significant unit elseif tfrac then local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil strval = format_fraction(parms, spell_inout, false, wholestr, tfrac.numstr, tfrac.denstr, do_spell) else strval = (thisvalue == 0) and from_en('0') or with_separator(parms, format(fmt, thisvalue)) if do_spell then strval = spell_number(parms, spell_inout, strval) or strval end end table.insert(results, strval .. sep1 .. id) if outvalue == 0 then break end fmt = '%.0f' -- only least significant unit can have a non-integral value end local reversed, count = {}, #results for i = 1, count do reversed[i] = results[count + 1 - i] end return true, sign .. table.concat(reversed, sep2) end local valinfo = in_unit_table.valinfo local success, result = make_result(valinfo[1], true) if not success then return false, result end local range = parms.range if range then for i = 1, range.n do local success, result2 = make_result(valinfo[i+1]) if not success then return false, result2 end result = range_text(range[i], want_name, parms, result, result2, inout, {spaced=true}) end end return true, result .. mid end local function process(parms, in_unit_table, out_unit_table) -- Return true, s, outunit where s = final wikitext result, -- or return false, t where t is an error message table. linked_pages = {} local success, bad_output local bad_input_mcode = in_unit_table.bad_mcode -- nil if input unit is a valid convert unit local out_unit = parms.out_unit if out_unit == nil or out_unit == '' or type(out_unit) == 'function' then if bad_input_mcode or parms.opt_input_unit_only then bad_output = '' else local getdef = type(out_unit) == 'function' and out_unit or get_default success, out_unit = getdef(in_unit_table.valinfo[1].value, in_unit_table) parms.out_unit = out_unit if not success then bad_output = out_unit end end end if not bad_output and not out_unit_table then success, out_unit_table = lookup(parms, out_unit, 'any_combination') if success then local mismatch = check_mismatch(in_unit_table, out_unit_table) if mismatch then bad_output = mismatch end else bad_output = out_unit_table end end local lhs, rhs local flipped = parms.opt_flip and not bad_input_mcode if bad_output then rhs = (bad_output == '') and '' or message(parms, bad_output) elseif parms.opt_input_unit_only then rhs = '' else local combos -- nil (for 'ft' or 'ftin'), or table of unit tables (for 'm ft') if not out_unit_table.multiple then -- nil/false ('ft' or 'm ft'), or table of factors ('ftin') combos = out_unit_table.combination end local frac = parms.frac -- nil or denominator of fraction for output values if frac then -- Apply fraction to the unit (if only one), or to non-SI units (if a combination), -- except that if a precision is also specified, the fraction only applies to -- the hand unit; that allows the following result: -- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands) -- However, the following is handled elsewhere as a special case: -- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in) if combos then local precision = parms.precision for _, unit in ipairs(combos) do if unit.builtin == 'hand' or (not precision and not unit.prefixes) then unit.frac = frac end end else out_unit_table.frac = frac end end local outputs = {} local imax = combos and #combos or 1 -- 1 (single unit) or number of unit tables if imax == 1 then parms.opt_order_out = nil -- only useful with an output combination end if not flipped and not parms.opt_order_out then -- Process left side first so any duplicate links (from lk=on) are suppressed -- on right. Example: {{convert|28|e9pc|e9ly|abbr=off|lk=on}} lhs = process_input(parms, in_unit_table) end for i = 1, imax do local success, item local out_current = combos and combos[i] or out_unit_table out_current.inout = 'out' if i == 1 then if imax > 1 and out_current.builtin == 'hand' then out_current.out_next = combos[2] -- built-in hand can influence next unit in a combination end if parms.opt_order_out then out_current.inout = 'in' end end if out_current.multiple then success, item = make_output_multiple(parms, in_unit_table, out_current) else success, item = make_output_single(parms, in_unit_table, out_current) end if not success then return false, item end outputs[i] = item end if parms.opt_order_out then lhs = outputs[1] table.remove(outputs, 1) end local sep = parms.table_joins and parms.table_joins[2] or parms.join_between rhs = table.concat(outputs, sep) end if flipped or not lhs then local input = process_input(parms, in_unit_table) if flipped then lhs = rhs rhs = input else lhs = input end end if parms.join_before then lhs = parms.join_before .. lhs end local wikitext if bad_input_mcode then if bad_input_mcode == '' then wikitext = lhs else wikitext = lhs .. message(parms, bad_input_mcode) end elseif parms.table_joins then wikitext = parms.table_joins[1] .. lhs .. parms.table_joins[2] .. rhs else wikitext = lhs .. parms.joins[1] .. rhs .. parms.joins[2] end if parms.warnings and not bad_input_mcode then wikitext = wikitext .. parms.warnings end return true, get_styles(parms) .. wikitext, out_unit_table end local function main_convert(frame) -- Do convert, and if needed, do it again with higher default precision. local parms = { frame = frame } -- will hold template arguments, after translation set_config(frame.args) local success, result = get_parms(parms, frame:getParent().args) if success then if type(result) ~= 'table' then return tostring(result) end local in_unit_table = result local out_unit_table for _ = 1, 2 do -- use counter so cannot get stuck repeating convert success, result, out_unit_table = process(parms, in_unit_table, out_unit_table) if success and parms.do_convert_again then parms.do_convert_again = false else break end end end -- If input=x gives a problem, the result should be just the user input -- (if x is a property like P123 it has been replaced with ''). -- An unknown input unit would display the input and an error message -- with success == true at this point. -- Also, can have success == false with a message that outputs an empty string. if parms.input_text then if success and not parms.have_problem then return result end local cat if parms.tracking then -- Add a tracking category using the given text as the category sort key. -- There is currently only one type of tracking, but in principle multiple -- items could be tracked, using different sort keys for convenience. cat = wanted_category('tracking', parms.tracking) end return parms.input_text .. (cat or '') end return success and result or message(parms, result) end local function _unit(unitcode, options) -- Helper function for Module:Val to look up a unit. -- Parameter unitcode must be a string to identify the wanted unit. -- Parameter options must be nil or a table with optional fields: -- value = number (for sort key; default value is 1) -- scaled_top = nil for a normal unit, or a number for a unit which is -- the denominator of a per unit (for sort key) -- si = { 'symbol', 'link' } -- (a table with two strings) to make an SI unit -- that will be used for the look up -- link = true if result should be [[linked]] -- sort = 'on' or 'debug' if result should include a sort key in a -- span element ('debug' makes the key visible) -- name = true for the name of the unit instead of the symbol -- us = true for the US spelling of the unit, if any -- Return nil if unitcode is not a non-empty string. -- Otherwise return a table with fields: -- text = requested symbol or name of unit, optionally linked -- scaled_value = input value adjusted by unit scale; used for sort key -- sortspan = span element with sort key like that provided by {{ntsh}}, -- calculated from the result of converting value -- to a base unit with scale 1. -- unknown = true if the unitcode was not known unitcode = strip(unitcode) if unitcode == nil or unitcode == '' then return nil end set_config({}) linked_pages = {} options = options or {} local parms = { abbr = options.name and 'off' or 'on', lk = options.link and 'on' or nil, opt_sp_us = options.us and true or nil, opt_ignore_error = true, -- do not add pages using this function to 'what links here' for Module:Convert/extra opt_sortable_on = options.sort == 'on' or options.sort == 'debug', opt_sortable_debug = options.sort == 'debug', } if options.si then -- Make a dummy table of units (just one unit) for lookup to use. -- This makes lookup recognize any SI prefix in the unitcode. local symbol = options.si[1] or '?' parms.unittable = { [symbol] = { _name1 = symbol, _name2 = symbol, _symbol = symbol, utype = symbol, scale = symbol == 'g' and 0.001 or 1, prefixes = 1, default = symbol, link = options.si[2], }} end local success, unit_table = lookup(parms, unitcode, 'no_combination') if not success then unit_table = setmetatable({ symbol = unitcode, name2 = unitcode, utype = unitcode, scale = 1, default = '', defkey = '', linkey = '' }, unit_mt) end local value = tonumber(options.value) or 1 local clean = tostring(abs(value)) local info = { value = value, altvalue = value, singular = (clean == '1'), clean = clean, show = clean, } unit_table.inout = 'in' unit_table.valinfo = { info } local sortspan, scaled_value if options.sort then sortspan, scaled_value = make_table_or_sort(parms, value, info, unit_table, options.scaled_top) end return { text = make_id(parms, 1, unit_table), sortspan = sortspan, scaled_value = scaled_value, unknown = not success and true or nil, } end return { convert = main_convert, _unit = _unit } cac541bea61c5fbbcb0a2768343935e97587b60a Template:Infobox/doc 10 338 760 2023-05-19T18:05:05Z wikipedia>Andrybak 0 add [[Module:Italic title]] to Lua wikitext text/x-wiki {{High-use|3308957|all-pages = yes}} {{module rating|protected}} {{Lua|Module:Navbar|Module:Italic title}} {{Uses TemplateStyles|Module:Infobox/styles.css|Template:Hlist/styles.css|Template:Plainlist/styles.css}} '''Module:Infobox''' is a [[WP:Module|module]] that implements the {{tl|Infobox}} template. Please see the template page for usage instructions. == Tracking categories == * {{clc|Pages using infobox templates with ignored data cells}} * {{clc|Articles using infobox templates with no data rows}} * {{clc|Pages using embedded infobox templates with the title parameter}} <includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox|| [[Category:Modules that add a tracking category]] [[Category:Wikipedia infoboxes]] [[Category:Infobox modules]] [[Category:Modules that check for strip markers]] }}</includeonly> 936ad219eb263a6f3293d62f667bd7b5db1059c1 671 2023-07-08T09:55:49Z wikipedia>Redrose64 0 Reverted edit by [[Special:Contribs/78.190.143.204|78.190.143.204]] ([[User talk:78.190.143.204|talk]]) to last version by Cedar101 wikitext text/x-wiki {{Documentation subpage}} <!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) --> {{distinguish|Template:Userbox}} {{#ifeq:{{SUBPAGENAME}}|sandbox||{{High-use}}}} {{Lua|Module:Infobox}} {{Parameter names example |name={{PAGENAME}} <!--|child |subbox |decat--> |title |above |subheader |subheader1 |subheader2={{{subheader2}}}<br/>...... |image|caption |image1|caption1 |image2|caption2={{{caption2}}}<br/>...... |header1=<div style="border-top:1px dashed #ccc;">{{{header1}}}<br/>{{nobold|( ''or'' )}}</div> |label2={{{label1}}} |data2={{{data1}}} |data3=( ''or'' ) |data4=<div style="padding-bottom:0.25em;border-bottom:1px dashed #ccc;">{{{data1}}}</div> |header5={{{header2}}}<br/><div style="padding:0.75em 0 0.5em;">{{nobold|( ''or'' )}}</div> |label6={{{label2}}} |data6={{{data2}}} |data7=( ''or'' ) |data8=<div style="padding-bottom:0.25em;border-bottom:1px dashed #ccc;">{{{data2}}}</div> |data9=<div style="padding:0.75em 0 0.5em;">( ''etc'' )</div> |below }} This template is intended as a meta template: a template used for constructing other templates. '''Note''': In general, it is not meant for use directly in an article, but can be used on a one-off basis if required. [[Help:Infobox]] contains an introduction about the recommended content and design of infoboxes; [[Wikipedia:Manual of Style/Infoboxes]] contains additional style guidelines. See [[WP:List of infoboxes]] and [[:Category:Infobox templates]] for lists of prepared topic-specific infoboxes. == Usage == {{tlf|Infobox}} is a meta-template: used to organise an actual <nowiki>{{Infobox sometopic}}</nowiki> template (like {{tl|Infobox building}}). For <code><nowiki>[[Template:Infobox sometopic]]</nowiki></code>, template code then looks like this, simplified: <syntaxhighlight lang="wikitext"> {{Infobox | name = {{{name|{{PAGENAME}}}}} | image = {{{image|}}} | caption1 = {{{caption|}}} | label1 = Former names | data1 = {{{former_names|}}} | header2 = General information | label3 = Status | data3 = {{{status|}}} ... <!-- etc. --> }} </syntaxhighlight> == Optional control parameters == ; name : If this parameter is present, "view/talk/edit" links will be added to the bottom of the infobox pointing to the named page, prefixed by <code>Template:</code> if no namespace is specified. You may use the value <nowiki>{{subst:PAGENAME}}</nowiki>; however, this is rarely what you want because it will send users clicking these links in an infobox to the template code rather than the data in the infobox they probably want to change. ; child : See the [[#Embedding|Embedding]] section for details. If this is set to "yes", this child infobox should be titled but have no name parameter. This parameter is empty by default, set it to "yes" to activate it. ; subbox : See the [[#Subboxes|Subboxes]] section for details. If this is set to "yes", this subbox should be titled but have no name parameter. This parameter is empty by default, set to "yes" to activate it. It has no effect if the '''child''' parameter is also set to "yes". ; decat : If this is set to "yes", the current page will not be autocategorized in a maintenance category when the generated infobox has some problems or no visible data section. Leave empty by default or set to "yes" to activate it. ; autoheaders: If this is set to any non-blank value, headers which are not followed by data fields are suppressed. See the "[[#Hiding headers when all its data fields are empty|hiding headers when all its data fields are empty]]" section for more details. == Content parameters == === Title === There are two different ways to put a title on an infobox. One contains the title inside the infobox's border in the uppermost cell of the table, the other puts it as a caption on top of the table. You can use them both together, or just one or the other, or neither (though this is not recommended): ; title : Text to put in the caption over the top of the table (or as section header before the whole content of this table, if this is a child infobox). For [[Wikipedia:Manual of Style/Accessibility#Tables|accessibility reasons]], this is the most recommended alternative. ; above : Text to put within the uppermost cell of the table. ; subheader(n) : additional title fields which fit below {{{title}}} and {{{above}}}, but before images. Examples: {{Infobox | name = Infobox/doc | title = Text in caption over infobox | subheader = Subheader of the infobox | header = (the rest of the infobox goes here) }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | title = Text in caption over infobox | subheader = Subheader of the infobox | header = (the rest of the infobox goes here) }} </syntaxhighlight>{{clear}} {{Infobox | name = Infobox/doc | above = Text in uppermost cell of infobox | subheader = Subheader of the infobox | subheader2 = Second subheader of the infobox | header = (the rest of the infobox goes here) }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | above = Text in uppermost cell of infobox | subheader = Subheader of the infobox | subheader2 = Second subheader of the infobox | header = (the rest of the infobox goes here) }} </syntaxhighlight>{{clear}} === Illustration images === ; image(n) : images to display at the top of the template. Use full image syntax, for example <nowiki>[[File:example.png|200px|alt=Example alt text]]</nowiki>. Image is centered by default. See [[WP:ALT]] for more on alt text. ; caption(n) : Text to put underneath the images. === Main data === ; header(n) : Text to use as a header in row n. ; label(n) : Text to use as a label in row n. ; data(n) : Text to display as data in row n. Note: for any given value for (n), not all combinations of parameters are permitted. The presence of a {{para|header''(n)''}} will cause the corresponding {{para|data''(n)''}} (and {{para|rowclass''(n)''}} {{para|label''(n)''}}, see below) to be ignored; the absence of a {{para|data''(n)''}} will cause the corresponding {{para|label''(n)''}} to be ignored. Valid combinations for any single row are: * {{para|class''(n)''}} {{para|header''(n)''}} * {{para|rowclass''(n)''}} {{para|class''(n)''}} {{para|data''(n)''}} * {{para|rowclass''(n)''}} {{para|label''(n)''}} {{para|class''(n)''}} {{para|data''(n)''}} See the rendering of header4, label4, and data4 in the [[#Examples|Examples]] section below. ==== Number ranges ==== To allow flexibility when the layout of an infobox is changed, it may be helpful when developing an infobox to use non-contiguous numbers for header and label/data rows. Parameters for new rows can then be inserted in future without having to renumber existing parameters. For example: <syntaxhighlight lang="wikitext" style="overflow:auto"> | header3 = Section 1 | label5 = Label A | data5 = Data A | label7 = Label C | data7 = Data C | header10 = Section 2 | label12 = Label D | data12 = Data D </syntaxhighlight>{{clear}} It is also possible to automatically renumber parameter names by using [[User:Frietjes/infoboxgap.js]] or [[Module:IncrementParams]]. There is no upper limit on numbers but there must be at most 50 between each used number. ==== Making data fields optional ==== A row with a label but no data is not displayed. This allows for the easy creation of optional infobox content rows. To make a row optional use a parameter that defaults to an empty string, like so: <syntaxhighlight lang="wikitext" style="overflow:auto"> | label5 = Population | data5 = {{{population|}}} </syntaxhighlight>{{clear}} This way if an article doesn't define the population parameter in its infobox the row won't be displayed. For more complex fields with pre-formatted contents that would still be present even if the parameter wasn't set, you can wrap it all in an "#if" statement to make the whole thing vanish when the parameter is not used. For instance, the "#if" statement in the following example reads "#if:the parameter ''mass'' has been supplied |then display it, followed by 'kg'": <syntaxhighlight lang="wikitext" style="overflow:auto"> | label6 = Mass | data6 = {{ #if: {{{mass|}}} | {{{mass}}} kg }} </syntaxhighlight>{{clear}} For more on #if, see [[meta:ParserFunctions##if:|here]]. ==== Hiding headers when all its data fields are empty ==== You can also make headers automatically hide when their section is empty (has no data-row showing). Consider this situation: {{Infobox | title = Example: header with & without data | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} </syntaxhighlight>{{clear}} If you want hide the header when no {{para|data''N''}} values are present, use '''{{para|autoheaders|y}}''': {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} </syntaxhighlight>{{clear}} So, header1 will be shown if any of item1, item2, or item3 is defined. If none of the three parameters are defined the header won't be shown and no empty row appears before the next visible content. Note: if the data has empty css elements, like {{para|data|2=&lt;span style="background:yellow;">&lt;/span>}}, this will be treated as non-empty (having data). If {{para|autoheaders|y}} but there are items that you ''do not'' want to trigger a header, place {{para|headerX|_BLANK_}}. This will serve as an empty header and separate it from the subsequent items. {{Infobox | title = Example: blank header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = _BLANK_ | label6 = label6 text | data6 = Some value, but does not trigger header1 or show header5 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = _BLANK_ | label6 = label6 text | data6 = Some value, but does not trigger header1 or show header5 }} </syntaxhighlight>{{clear}} === Footer === ; below : Text to put in the bottom cell. The bottom cell is intended for footnotes, see-also, and other such information. == Presentation parameters == === Italic titles === Titles of articles with infoboxes may be made italic, in line with [[WP:ITALICTITLE]], by passing the <code>italic title</code> parameter. * Turn on italic titles by passing {{para|italic title|<nowiki>{{{italic title|}}}</nowiki>}} from the infobox. * Turn off by default (notably because only Latin script may be safely rendered in this style and italic may be needed to distinguish foreign language from local English language only in that script, but would be difficult to read for other scripts) but allow some instances to be made italic by passing {{para|italic title|<nowiki>{{{italic title|no}}}</nowiki>}} * Do not make any titles italic by not passing the parameter at all. === CSS styling === {{div col}} ; bodystyle : Applies to the infobox table as a whole ; titlestyle : Applies only to the title caption. Adding a background color is usually inadvisable since the text is rendered "outside" the infobox. ; abovestyle : Applies only to the "above" cell at the top. The default style has font-size:125%; since this cell is usually used for a title, if you want to use the above cell for regular-sized text include "font-size:100%;" in the abovestyle. ; imagestyle : Applies to the cell the image is in. This includes the text of the image caption, but you should set text properties with captionstyle instead of imagestyle in case the caption is moved out of this cell in the future. ; captionstyle : Applies to the text of the image caption. ; rowstyle(n) : This parameter is inserted into the <code>style</code> attribute for the specified row. ; headerstyle : Applies to all header cells ; subheaderstyle : Applies to all subheader cells ; labelstyle : Applies to all label cells ; datastyle : Applies to all data cells ; belowstyle : Applies only to the below cell {{div col end}} === HTML classes and microformats === {{div col}} ; bodyclass : This parameter is inserted into the <code>class</code> attribute for the infobox as a whole. ; titleclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''title''' caption. <!-- currently not implemented in Lua module ; aboverowclass : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''above''' cell is on. --> ; aboveclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''above''' cell. ; subheaderrowclass(n) : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''subheader''' is on. ; subheaderclass(n) : This parameter is inserted into the <code>class</code> attribute for the infobox's '''subheader'''. ; imagerowclass(n) : These parameters are inserted into the <code>class</code> attribute for the complete table row their respective '''image''' is on. ; imageclass : This parameter is inserted into the <code>class</code> attribute for the '''image'''. ; rowclass(n) : This parameter is inserted into the <code>class</code> attribute for the specified row including the '''label''' and '''data''' cells. ; class(n) : This parameter is inserted into the <code>class</code> attribute for the '''data''' cell of the specified row. If there's no '''data''' cell it has no effect. <!-- currently not implemented in Lua module ; belowrowclass : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''below''' cell is on. --> ; belowclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''below''' cell. {{div col end}} This template supports the addition of microformat information. This is done by adding "class" attributes to various data cells, indicating what kind of information is contained within. Multiple class names may be specified, separated by spaces, some of them being used as selectors for custom styling according to a project policy or to the skin selected in user preferences, others being used for microformats. To flag an infobox as containing [[hCard]] information, for example, add the following parameter: <syntaxhighlight lang="wikitext" style="overflow:auto"> | bodyclass = vcard </syntaxhighlight>{{clear}} And for each row containing a data cell that's part of the vcard, add a corresponding class parameter: <syntaxhighlight lang="wikitext" style="overflow:auto"> | class1 = fn | class2 = org | class3 = tel </syntaxhighlight>{{clear}} ...and so forth. "above" and "title" can also be given classes, since these are usually used to display the name of the subject of the infobox. See [[Wikipedia:WikiProject Microformats]] for more information on adding microformat information to Wikipedia, and [[microformat]] for more information on microformats in general. == Examples == Notice how the row doesn't appear in the displayed infobox when a '''label''' is defined without an accompanying '''data''' cell, and how all of them are displayed when a '''header''' is defined on the same row as a '''data''' cell. Also notice that '''subheaders''' are not bold by default like the '''headers''' used to split the main data section, because this role is meant to be for the '''above''' cell : {{Infobox |name = Infobox/doc |bodystyle = |titlestyle = |abovestyle = background:#cfc; |subheaderstyle = |title = Test Infobox |above = Above text |subheader = Subheader above image |subheader2 = Second subheader |imagestyle = |captionstyle = |image = [[File:Example-serious.jpg|200px|alt=Example alt text]] |caption = Caption displayed below File:Example-serious.jpg |headerstyle = background:#ccf; |labelstyle = background:#ddf; |datastyle = |header1 = Header defined alone | label1 = | data1 = |header2 = | label2 = Label defined alone does not display (needs data, or is suppressed) | data2 = |header3 = | label3 = | data3 = Data defined alone |header4 = All three defined (header, label, data, all with same number) | label4 = does not display (same number as a header) | data4 = does not display (same number as a header) |header5 = | label5 = Label and data defined (label) | data5 = Label and data defined (data) |belowstyle = background:#ddf; |below = Below text }} <syntaxhighlight lang="wikitext"> {{Infobox |name = Infobox/doc |bodystyle = |titlestyle = |abovestyle = background:#cfc; |subheaderstyle = |title = Test Infobox |above = Above text |subheader = Subheader above image |subheader2 = Second subheader |imagestyle = |captionstyle = |image = [[File:Example-serious.jpg|200px|alt=Example alt text]] |caption = Caption displayed below File:Example-serious.jpg |headerstyle = background:#ccf; |labelstyle = background:#ddf; |datastyle = |header1 = Header defined alone | label1 = | data1 = |header2 = | label2 = Label defined alone does not display (needs data, or is suppressed) | data2 = |header3 = | label3 = | data3 = Data defined alone |header4 = All three defined (header, label, data, all with same number) | label4 = does not display (same number as a header) | data4 = does not display (same number as a header) |header5 = | label5 = Label and data defined (label) | data5 = Label and data defined (data) |belowstyle = background:#ddf; |below = Below text }} </syntaxhighlight> For this example, the {{para|bodystyle}} and {{para|labelstyle}} parameters are used to adjust the infobox width and define a default width for the column of labels: {{Infobox |name = Infobox/doc |bodystyle = width:20em |titlestyle = |title = Test Infobox |headerstyle = |labelstyle = width:33% |datastyle = |header1 = | label1 = Label 1 | data1 = Data 1 |header2 = | label2 = Label 2 | data2 = Data 2 |header3 = | label3 = Label 3 | data3 = Data 3 |header4 = Header 4 | label4 = | data4 = |header5 = | label5 = Label 5 | data5 = Data 5: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |belowstyle = |below = Below text }} <syntaxhighlight lang="wikitext"> {{Infobox |name = Infobox/doc |bodystyle = width:20em |titlestyle = |title = Test Infobox |headerstyle = |labelstyle = width:33% |datastyle = |header1 = | label1 = Label 1 | data1 = Data 1 |header2 = | label2 = Label 2 | data2 = Data 2 |header3 = | label3 = Label 3 | data3 = Data 3 |header4 = Header 4 | label4 = | data4 = |header5 = | label5 = Label 5 | data5 = Data 5: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |belowstyle = |below = Below text }} </syntaxhighlight> == Embedding == <!--Linked from [[Template:Subinfobox bodystyle/doc]]--> One infobox template can be embedded into another using the {{para|child}} parameter. This feature can be used to create a modular infobox, or to create better-defined logical sections. Long ago, it was necessary to use embedding in order to create infoboxes with more than 99 rows; but nowadays there's no limit to the number of rows that can be defined in a single instance of <code><nowiki>{{infobox}}</nowiki></code>. {{Infobox | title = Top level title | data1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | data2 = {{Infobox | decat = yes | child = yes |title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | data1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | data2 = {{Infobox | decat = yes | child = yes |title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} Note, in the examples above, the child infobox is placed in a <code>data</code> field, not a <code>header</code> field. Notice that the section subheadings are not in bold font if bolding is not explicitly specified. To obtain bold section headings, place the child infobox in a '''header''' field (but not in a '''label''' field because it would not be displayed!), either using {{Infobox | title = Top level title | header1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | header2 = {{Infobox | decat = yes | child = yes | title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | header1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | header2 = {{Infobox | decat = yes | child = yes | title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} or, {{Infobox | title = Top level title | header1 = First subsection {{Infobox | decat = yes | child = yes | label1 = Label 1.1 | data1 = Data 1.1 }} | header2 = Second subsection {{Infobox | decat = yes | child = yes | label1 = Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | header1 = First subsection {{Infobox | decat = yes | child = yes | label1 = Label 1.1 | data1 = Data 1.1 }} | header2 = Second subsection {{Infobox | decat = yes | child = yes | label1 = Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} Note that omitting the {{para|title}} parameter, and not including any text preceding the embedded infobox, may result in spurious blank table rows, creating gaps in the visual presentation. The garbage output can be suppressed using {{para|rowstyleN|display: none}}, replacing N with the data/header number. [[Wikipedia:WikiProject Infoboxes/embed]] includes some links to Wikipedia articles which include infoboxes embedded within other infoboxes. == Subboxes == An alternative method for embedding is to use {{para|subbox|yes}}, which removes the outer border from the infobox, but preserves the interior structure. One feature of this approach is that the parent and child boxes need not have the same structure, and the label and data fields are not aligned between the parent and child boxes because they are not in the same parent table. {{Infobox | headerstyle = background-color:#eee; | labelstyle = background-color:#eee; | header1 = Main 1 | header2 = Main 2 | data3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | data4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | label5 = Label 5 | data5 = Data 5 | header6 = Main 6 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | headerstyle = background-color:#eee; | labelstyle = background-color:#eee; | header1 = Main 1 | header2 = Main 2 | data3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | data4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | label5 = Label 5 | data5 = Data 5 | header6 = Main 6 }} </syntaxhighlight>{{clear}} Similar embedding techniques may be used within content parameters of some other templates generating tables (such as [[:Template:Sidebar|Sidebar]]) : {{Sidebar | navbar = off | headingstyle = background-color:#eee; | heading1 = Heading 1 | heading2 = Heading 2 | content3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | content4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | heading5 = Heading 5 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Sidebar | navbar = off | headingstyle = background-color:#eee; | heading1 = Heading 1 | heading2 = Heading 2 | content3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | content4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | heading5 = Heading 5 }} </syntaxhighlight>{{clear}} Note that the default padding of the parent data cell containing each subbox is still visible, so the subboxes are slightly narrower than the parent box and there's a higher vertical spacing between standard cells of the parent box than between cells of distinct subboxes. == Controlling line-breaking in embedded bulletless lists == Template {{tlx|nbsp}} may be used with {{tlx|wbr}} and {{tlx|nowrap}} to control line-breaking in bulletless lists embedded in infoboxes (e.g. cast list in {{tlx|Infobox film}}), to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details. == Full blank syntax == (Note: there is no limit to the number of possible rows; only 20 are given below since infoboxes larger than that will be relatively rare. Just extend the numbering as needed. The microformat "class" parameters are also omitted as they are not commonly used.) <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | child = {{{child|}}} | subbox = {{{subbox|}}} | italic title = {{{italic title|no}}} | templatestyles = | child templatestyles = | grandchild templatestyles = | bodystyle = | titlestyle = | abovestyle = | subheaderstyle = | title = | above = | subheader = | imagestyle = | captionstyle = | image = | caption = | image2 = | caption2 = | headerstyle = | labelstyle = | datastyle = | header1 = | label1 = | data1 = | header2 = | label2 = | data2 = | header3 = | label3 = | data3 = | header4 = | label4 = | data4 = | header5 = | label5 = | data5 = | header6 = | label6 = | data6 = | header7 = | label7 = | data7 = | header8 = | label8 = | data8 = | header9 = | label9 = | data9 = | header10 = | label10 = | data10 = | header11 = | label11 = | data11 = | header12 = | label12 = | data12 = | header13 = | label13 = | data13 = | header14 = | label14 = | data14 = | header15 = | label15 = | data15 = | header16 = | label16 = | data16 = | header17 = | label17 = | data17 = | header18 = | label18 = | data18 = | header19 = | label19 = | data19 = | header20 = | label20 = | data20 = | belowstyle = | below = }} </syntaxhighlight>{{clear}} {{Help:Infobox/user style}} == Porting to other MediaWikis == The infobox template requires the [[:mw:Extension:Scribunto|Scribunto]] extension. [[Wikipedia:WikiProject Transwiki|WikiProject Transwiki]] has a version of this template that has been modified to work on other MediaWikis. == TemplateData == {{TemplateData header}} <templatedata> { "description": "This template is intended as a meta template, a template used for constructing other templates. In general, it is not meant for use directly in an article but can be used on a one-off basis if required.", "format": "{{_\n| ________________ = _\n}}\n", "params": { "title": { "label": "Title", "description": "Title displayed above the infobox", "type": "string", "suggested": true }, "image": { "label": "Image", "description": "Image illustrating the topic. Use full image syntax.", "type": "content", "suggested": true, "example": "[[File:example.png|200px|alt=Example alt text]]" }, "caption": { "label": "Caption", "description": "caption for the image", "type": "content", "suggested": true } }, "paramOrder": [ "title", "image", "caption" ] } </templatedata> ==Tracking categories== * {{Category link with count|Articles with missing Wikidata information}} * {{Category link with count|Articles using infobox templates with no data rows}} * {{Category link with count|Pages using embedded infobox templates with the title parameter}} ==See also== * [[Module:Infobox]], the [[WP:LUA|Lua]] module on which this template is based * [[Module:Check for unknown parameters]] * {{tl|Infobox3cols}} * {{tl|Navbox}} and {{tl|Sidebar}} * [[Wikipedia:List of infoboxes|List of infoboxes]] * [[:Module:InfoboxImage]] <includeonly>{{Sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:Infobox templates| ]] [[Category:Wikipedia metatemplates|Infobox]] [[Category:Templates generating microformats]] [[Category:Templates that add a tracking category]] [[Category:Templates based on the Infobox Lua module]] }}</includeonly> 7b5cc59c733eab17e47789808768a3f1064804b1 Module:Infobox/doc 828 387 774 2023-05-19T18:05:05Z wikipedia>Andrybak 0 add [[Module:Italic title]] to Lua wikitext text/x-wiki {{High-use|3308957|all-pages = yes}} {{module rating|protected}} {{Lua|Module:Navbar|Module:Italic title}} {{Uses TemplateStyles|Module:Infobox/styles.css|Template:Hlist/styles.css|Template:Plainlist/styles.css}} '''Module:Infobox''' is a [[WP:Module|module]] that implements the {{tl|Infobox}} template. Please see the template page for usage instructions. == Tracking categories == * {{clc|Pages using infobox templates with ignored data cells}} * {{clc|Articles using infobox templates with no data rows}} * {{clc|Pages using embedded infobox templates with the title parameter}} <includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox|| [[Category:Modules that add a tracking category]] [[Category:Wikipedia infoboxes]] [[Category:Infobox modules]] [[Category:Modules that check for strip markers]] }}</includeonly> 936ad219eb263a6f3293d62f667bd7b5db1059c1 Module:Navbox 828 443 886 2023-05-28T13:26:56Z wikipedia>Uzume 0 strict Scribunto text/plain require('strict') local p = {} local navbar = require('Module:Navbar')._navbar local cfg = mw.loadData('Module:Navbox/configuration') local getArgs -- lazily initialized local args local format = string.format local function striped(wikitext, border) -- Return wikitext with markers replaced for odd/even striping. -- Child (subgroup) navboxes are flagged with a category that is removed -- by parent navboxes. The result is that the category shows all pages -- where a child navbox is not contained in a parent navbox. local orphanCat = cfg.category.orphan if border == cfg.keyword.border_subgroup and args[cfg.arg.orphan] ~= cfg.keyword.orphan_yes then -- No change; striping occurs in outermost navbox. return wikitext .. orphanCat end local first, second = cfg.class.navbox_odd_part, cfg.class.navbox_even_part if args[cfg.arg.evenodd] then if args[cfg.arg.evenodd] == cfg.keyword.evenodd_swap then first, second = second, first else first = args[cfg.arg.evenodd] second = first end end local changer if first == second then changer = first else local index = 0 changer = function (code) if code == '0' then -- Current occurrence is for a group before a nested table. -- Set it to first as a valid although pointless class. -- The next occurrence will be the first row after a title -- in a subgroup and will also be first. index = 0 return first end index = index + 1 return index % 2 == 1 and first or second end end local regex = orphanCat:gsub('([%[%]])', '%%%1') return (wikitext:gsub(regex, ''):gsub(cfg.marker.regex, changer)) -- () omits gsub count end local function processItem(item, nowrapitems) if item:sub(1, 2) == '{|' then -- Applying nowrap to lines in a table does not make sense. -- Add newlines to compensate for trim of x in |parm=x in a template. return '\n' .. item ..'\n' end if nowrapitems == cfg.keyword.nowrapitems_yes then local lines = {} for line in (item .. '\n'):gmatch('([^\n]*)\n') do local prefix, content = line:match('^([*:;#]+)%s*(.*)') if prefix and not content:match(cfg.pattern.nowrap) then line = format(cfg.nowrap_item, prefix, content) end table.insert(lines, line) end item = table.concat(lines, '\n') end if item:match('^[*:;#]') then return '\n' .. item ..'\n' end return item end local function has_navbar() return args[cfg.arg.navbar] ~= cfg.keyword.navbar_off and args[cfg.arg.navbar] ~= cfg.keyword.navbar_plain and ( args[cfg.arg.name] or mw.getCurrentFrame():getParent():getTitle():gsub(cfg.pattern.sandbox, '') ~= cfg.pattern.navbox ) end local function renderNavBar(titleCell) if has_navbar() then titleCell:wikitext(navbar{ [cfg.navbar.name] = args[cfg.arg.name], [cfg.navbar.mini] = 1, [cfg.navbar.fontstyle] = (args[cfg.arg.basestyle] or '') .. ';' .. (args[cfg.arg.titlestyle] or '') .. ';background:none transparent;border:none;box-shadow:none;padding:0;' }) end end local function renderTitleRow(tbl) if not args[cfg.arg.title] then return end local titleRow = tbl:tag('tr') local titleCell = titleRow:tag('th'):attr('scope', 'col') local titleColspan = 2 if args[cfg.arg.imageleft] then titleColspan = titleColspan + 1 end if args[cfg.arg.image] then titleColspan = titleColspan + 1 end titleCell :cssText(args[cfg.arg.basestyle]) :cssText(args[cfg.arg.titlestyle]) :addClass(cfg.class.navbox_title) :attr('colspan', titleColspan) renderNavBar(titleCell) titleCell :tag('div') -- id for aria-labelledby attribute :attr('id', mw.uri.anchorEncode(args[cfg.arg.title])) :addClass(args[cfg.arg.titleclass]) :css('font-size', '114%') :css('margin', '0 4em') :wikitext(processItem(args[cfg.arg.title])) end local function getAboveBelowColspan() local ret = 2 if args[cfg.arg.imageleft] then ret = ret + 1 end if args[cfg.arg.image] then ret = ret + 1 end return ret end local function renderAboveRow(tbl) if not args[cfg.arg.above] then return end tbl:tag('tr') :tag('td') :addClass(cfg.class.navbox_abovebelow) :addClass(args[cfg.arg.aboveclass]) :cssText(args[cfg.arg.basestyle]) :cssText(args[cfg.arg.abovestyle]) :attr('colspan', getAboveBelowColspan()) :tag('div') -- id for aria-labelledby attribute, if no title :attr('id', (not args[cfg.arg.title]) and mw.uri.anchorEncode(args[cfg.arg.above]) or nil) :wikitext(processItem(args[cfg.arg.above], args[cfg.arg.nowrapitems])) end local function renderBelowRow(tbl) if not args[cfg.arg.below] then return end tbl:tag('tr') :tag('td') :addClass(cfg.class.navbox_abovebelow) :addClass(args[cfg.arg.belowclass]) :cssText(args[cfg.arg.basestyle]) :cssText(args[cfg.arg.belowstyle]) :attr('colspan', getAboveBelowColspan()) :tag('div') :wikitext(processItem(args[cfg.arg.below], args[cfg.arg.nowrapitems])) end local function renderListRow(tbl, index, listnum, listnums_size) local row = tbl:tag('tr') if index == 1 and args[cfg.arg.imageleft] then row :tag('td') :addClass(cfg.class.noviewer) :addClass(cfg.class.navbox_image) :addClass(args[cfg.arg.imageclass]) :css('width', '1px') -- Minimize width :css('padding', '0 2px 0 0') :cssText(args[cfg.arg.imageleftstyle]) :attr('rowspan', listnums_size) :tag('div') :wikitext(processItem(args[cfg.arg.imageleft])) end local group_and_num = format(cfg.arg.group_and_num, listnum) local groupstyle_and_num = format(cfg.arg.groupstyle_and_num, listnum) if args[group_and_num] then local groupCell = row:tag('th') -- id for aria-labelledby attribute, if lone group with no title or above if listnum == 1 and not (args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group2]) then groupCell :attr('id', mw.uri.anchorEncode(args[cfg.arg.group1])) end groupCell :attr('scope', 'row') :addClass(cfg.class.navbox_group) :addClass(args[cfg.arg.groupclass]) :cssText(args[cfg.arg.basestyle]) -- If groupwidth not specified, minimize width :css('width', args[cfg.arg.groupwidth] or '1%') groupCell :cssText(args[cfg.arg.groupstyle]) :cssText(args[groupstyle_and_num]) :wikitext(args[group_and_num]) end local listCell = row:tag('td') if args[group_and_num] then listCell :addClass(cfg.class.navbox_list_with_group) else listCell:attr('colspan', 2) end if not args[cfg.arg.groupwidth] then listCell:css('width', '100%') end local rowstyle -- usually nil so cssText(rowstyle) usually adds nothing if index % 2 == 1 then rowstyle = args[cfg.arg.oddstyle] else rowstyle = args[cfg.arg.evenstyle] end local list_and_num = format(cfg.arg.list_and_num, listnum) local listText = args[list_and_num] local oddEven = cfg.marker.oddeven if listText:sub(1, 12) == '</div><table' then -- Assume list text is for a subgroup navbox so no automatic striping for this row. oddEven = listText:find(cfg.pattern.navbox_title) and cfg.marker.restart or cfg.class.navbox_odd_part end local liststyle_and_num = format(cfg.arg.liststyle_and_num, listnum) local listclass_and_num = format(cfg.arg.listclass_and_num, listnum) listCell :css('padding', '0') :cssText(args[cfg.arg.liststyle]) :cssText(rowstyle) :cssText(args[liststyle_and_num]) :addClass(cfg.class.navbox_list) :addClass(cfg.class.navbox_part .. oddEven) :addClass(args[cfg.arg.listclass]) :addClass(args[listclass_and_num]) :tag('div') :css('padding', (index == 1 and args[cfg.arg.list1padding]) or args[cfg.arg.listpadding] or '0 0.25em' ) :wikitext(processItem(listText, args[cfg.arg.nowrapitems])) if index == 1 and args[cfg.arg.image] then row :tag('td') :addClass(cfg.class.noviewer) :addClass(cfg.class.navbox_image) :addClass(args[cfg.arg.imageclass]) :css('width', '1px') -- Minimize width :css('padding', '0 0 0 2px') :cssText(args[cfg.arg.imagestyle]) :attr('rowspan', listnums_size) :tag('div') :wikitext(processItem(args[cfg.arg.image])) end end local function has_list_class(htmlclass) local patterns = { '^' .. htmlclass .. '$', '%s' .. htmlclass .. '$', '^' .. htmlclass .. '%s', '%s' .. htmlclass .. '%s' } for arg, _ in pairs(args) do if type(arg) == 'string' and mw.ustring.find(arg, cfg.pattern.class) then for _, pattern in ipairs(patterns) do if mw.ustring.find(args[arg] or '', pattern) then return true end end end end return false end -- there are a lot of list classes in the wild, so we add their TemplateStyles local function add_list_styles() local frame = mw.getCurrentFrame() local function add_list_templatestyles(htmlclass, templatestyles) if has_list_class(htmlclass) then return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } else return '' end end local hlist_styles = add_list_templatestyles('hlist', cfg.hlist_templatestyles) local plainlist_styles = add_list_templatestyles('plainlist', cfg.plainlist_templatestyles) -- a second workaround for [[phab:T303378]] -- when that issue is fixed, we can actually use has_navbar not to emit the -- tag here if we want if has_navbar() and hlist_styles == '' then hlist_styles = frame:extensionTag{ name = 'templatestyles', args = { src = cfg.hlist_templatestyles } } end -- hlist -> plainlist is best-effort to preserve old Common.css ordering. -- this ordering is not a guarantee because most navboxes will emit only -- one of these classes [hlist_note] return hlist_styles .. plainlist_styles end local function needsHorizontalLists(border) if border == cfg.keyword.border_subgroup or args[cfg.arg.tracking] == cfg.keyword.tracking_no then return false end return not has_list_class(cfg.pattern.hlist) and not has_list_class(cfg.pattern.plainlist) end local function hasBackgroundColors() for _, key in ipairs({cfg.arg.titlestyle, cfg.arg.groupstyle, cfg.arg.basestyle, cfg.arg.abovestyle, cfg.arg.belowstyle}) do if tostring(args[key]):find('background', 1, true) then return true end end return false end local function hasBorders() for _, key in ipairs({cfg.arg.groupstyle, cfg.arg.basestyle, cfg.arg.abovestyle, cfg.arg.belowstyle}) do if tostring(args[key]):find('border', 1, true) then return true end end return false end local function isIllegible() local styleratio = require('Module:Color contrast')._styleratio for key, style in pairs(args) do if tostring(key):match(cfg.pattern.style) then if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then return true end end end return false end local function getTrackingCategories(border) local cats = {} if needsHorizontalLists(border) then table.insert(cats, cfg.category.horizontal_lists) end if hasBackgroundColors() then table.insert(cats, cfg.category.background_colors) end if isIllegible() then table.insert(cats, cfg.category.illegible) end if hasBorders() then table.insert(cats, cfg.category.borders) end return cats end local function renderTrackingCategories(builder, border) local title = mw.title.getCurrentTitle() if title.namespace ~= 10 then return end -- not in template space local subpage = title.subpageText if subpage == cfg.keyword.subpage_doc or subpage == cfg.keyword.subpage_sandbox or subpage == cfg.keyword.subpage_testcases then return end for _, cat in ipairs(getTrackingCategories(border)) do builder:wikitext('[[Category:' .. cat .. ']]') end end local function renderMainTable(border, listnums) local tbl = mw.html.create('table') :addClass(cfg.class.nowraplinks) :addClass(args[cfg.arg.bodyclass]) local state = args[cfg.arg.state] if args[cfg.arg.title] and state ~= cfg.keyword.state_plain and state ~= cfg.keyword.state_off then if state == cfg.keyword.state_collapsed then state = cfg.class.collapsed end tbl :addClass(cfg.class.collapsible) :addClass(state or cfg.class.autocollapse) end tbl:css('border-spacing', 0) if border == cfg.keyword.border_subgroup or border == cfg.keyword.border_none then tbl :addClass(cfg.class.navbox_subgroup) :cssText(args[cfg.arg.bodystyle]) :cssText(args[cfg.arg.style]) else -- regular navbox - bodystyle and style will be applied to the wrapper table tbl :addClass(cfg.class.navbox_inner) :css('background', 'transparent') :css('color', 'inherit') end tbl:cssText(args[cfg.arg.innerstyle]) renderTitleRow(tbl) renderAboveRow(tbl) local listnums_size = #listnums for i, listnum in ipairs(listnums) do renderListRow(tbl, i, listnum, listnums_size) end renderBelowRow(tbl) return tbl end local function add_navbox_styles(hiding_templatestyles) local frame = mw.getCurrentFrame() -- This is a lambda so that it doesn't need the frame as a parameter local function add_user_styles(templatestyles) if templatestyles and templatestyles ~= '' then return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } end return '' end -- get templatestyles. load base from config so that Lua only needs to do -- the work once of parser tag expansion local base_templatestyles = cfg.templatestyles local templatestyles = add_user_styles(args[cfg.arg.templatestyles]) local child_templatestyles = add_user_styles(args[cfg.arg.child_templatestyles]) -- The 'navbox-styles' div exists to wrap the styles to work around T200206 -- more elegantly. Instead of combinatorial rules, this ends up being linear -- number of CSS rules. return mw.html.create('div') :addClass(cfg.class.navbox_styles) :wikitext( add_list_styles() .. -- see [hlist_note] applied to 'before base_templatestyles' base_templatestyles .. templatestyles .. child_templatestyles .. table.concat(hiding_templatestyles) ) :done() end -- work around [[phab:T303378]] -- for each arg: find all the templatestyles strip markers, insert them into a -- table. then remove all templatestyles markers from the arg local function move_hiding_templatestyles(args) local gfind = string.gfind local gsub = string.gsub local templatestyles_markers = {} local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)' for k, arg in pairs(args) do for marker in gfind(arg, strip_marker_pattern) do table.insert(templatestyles_markers, marker) end args[k] = gsub(arg, strip_marker_pattern, '') end return templatestyles_markers end function p._navbox(navboxArgs) args = navboxArgs local hiding_templatestyles = move_hiding_templatestyles(args) local listnums = {} for k, _ in pairs(args) do if type(k) == 'string' then local listnum = k:match(cfg.pattern.listnum) if listnum then table.insert(listnums, tonumber(listnum)) end end end table.sort(listnums) local border = mw.text.trim(args[cfg.arg.border] or args[1] or '') if border == cfg.keyword.border_child then border = cfg.keyword.border_subgroup end -- render the main body of the navbox local tbl = renderMainTable(border, listnums) local res = mw.html.create() -- render the appropriate wrapper for the navbox, based on the border param if border == cfg.keyword.border_none then res:node(add_navbox_styles(hiding_templatestyles)) local nav = res:tag('div') :attr('role', 'navigation') :node(tbl) -- aria-labelledby title, otherwise above, otherwise lone group if args[cfg.arg.title] or args[cfg.arg.above] or (args[cfg.arg.group1] and not args[cfg.arg.group2]) then nav:attr( 'aria-labelledby', mw.uri.anchorEncode( args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group1] ) ) else nav:attr('aria-label', cfg.aria_label) end elseif border == cfg.keyword.border_subgroup then -- We assume that this navbox is being rendered in a list cell of a -- parent navbox, and is therefore inside a div with padding:0em 0.25em. -- We start with a </div> to avoid the padding being applied, and at the -- end add a <div> to balance out the parent's </div> res :wikitext('</div>') :node(tbl) :wikitext('<div>') else res:node(add_navbox_styles(hiding_templatestyles)) local nav = res:tag('div') :attr('role', 'navigation') :addClass(cfg.class.navbox) :addClass(args[cfg.arg.navboxclass]) :cssText(args[cfg.arg.bodystyle]) :cssText(args[cfg.arg.style]) :css('padding', '3px') :node(tbl) -- aria-labelledby title, otherwise above, otherwise lone group if args[cfg.arg.title] or args[cfg.arg.above] or (args[cfg.arg.group1] and not args[cfg.arg.group2]) then nav:attr( 'aria-labelledby', mw.uri.anchorEncode(args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group1]) ) else nav:attr('aria-label', cfg.aria_label) end end if (args[cfg.arg.nocat] or cfg.keyword.nocat_false):lower() == cfg.keyword.nocat_false then renderTrackingCategories(res, border) end return striped(tostring(res), border) end function p.navbox(frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end args = getArgs(frame, {wrappers = {cfg.pattern.navbox}}) -- Read the arguments in the order they'll be output in, to make references -- number in the right order. local _ _ = args[cfg.arg.title] _ = args[cfg.arg.above] -- Limit this to 20 as covering 'most' cases (that's a SWAG) and because -- iterator approach won't work here for i = 1, 20 do _ = args[format(cfg.arg.group_and_num, i)] _ = args[format(cfg.arg.list_and_num, i)] end _ = args[cfg.arg.below] return p._navbox(args) end return p 05be9a97c035ab3f0fac69423779e261949d473c Template:High-use 10 332 659 2023-05-30T09:39:48Z wikipedia>Lectonar 0 Changed protection settings for "[[Template:High-use]]": [[WP:High-risk templates|High-risk template or module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) wikitext text/x-wiki {{#invoke:High-use|main|1={{{1|}}}|2={{{2|}}}|info={{{info|}}}|demo={{{demo|}}}|form={{{form|}}}|expiry={{{expiry|}}}|system={{{system|}}}}}<noinclude> {{Documentation}} <!-- Add categories to the /doc subpage; interwiki links go to Wikidata, thank you! --> </noinclude> a3322d1bd47ac03df14fa2090855cff4fede9bc7 Module:High-use 828 364 766 2023-05-30T09:39:48Z wikipedia>Lectonar 0 Changed protection settings for "[[Template:High-use]]": [[WP:High-risk templates|High-risk template or module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) wikitext text/x-wiki {{#invoke:High-use|main|1={{{1|}}}|2={{{2|}}}|info={{{info|}}}|demo={{{demo|}}}|form={{{form|}}}|expiry={{{expiry|}}}|system={{{system|}}}}}<noinclude> {{Documentation}} <!-- Add categories to the /doc subpage; interwiki links go to Wikidata, thank you! --> </noinclude> a3322d1bd47ac03df14fa2090855cff4fede9bc7 722 2023-05-30T11:20:32Z wikipedia>Lectonar 0 Changed protection settings for "[[Module:High-use]]": [[WP:High-risk templates|High-risk template or module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain local p = {} -- _fetch looks at the "demo" argument. local _fetch = require('Module:Transclusion_count').fetch local yesno = require('Module:Yesno') function p.num(frame, count) if count == nil then if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end end -- Build output string local return_value = "" if count == nil then if frame.args[1] == "risk" then return_value = "a very large number of" else return_value = "many" end else -- Use 2 significant figures for smaller numbers and 3 for larger ones local sigfig = 2 if count >= 100000 then sigfig = 3 end -- Prepare to round to appropriate number of sigfigs local f = math.floor(math.log10(count)) - sigfig + 1 -- Round and insert "approximately" or "+" when appropriate if (frame.args[2] == "yes") or (mw.ustring.sub(frame.args[1],-1) == "+") then -- Round down return_value = string.format("%s+", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) ) * (10^(f))) ) else -- Round to nearest return_value = string.format("approximately&#x20;%s", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) + 0.5) * (10^(f))) ) end -- Insert percentage of pages if that is likely to be >= 1% and when |no-percent= not set to yes if count and count > 250000 and not yesno (frame:getParent().args['no-percent']) then local percent = math.floor( ( (count/frame:callParserFunction('NUMBEROFPAGES', 'R') ) * 100) + 0.5) if percent >= 1 then return_value = string.format("%s&#x20;pages, or roughly %s%% of all", return_value, percent) end end end return return_value end -- Actions if there is a large (greater than or equal to 100,000) transclusion count function p.risk(frame) local return_value = "" if frame.args[1] == "risk" then return_value = "risk" else local count = _fetch(frame) if count and count >= 100000 then return_value = "risk" end end return return_value end function p.text(frame, count) -- Only show the information about how this template gets updated if someone -- is actually editing the page and maybe trying to update the count. local bot_text = (frame:preprocess("{{REVISIONID}}") == "") and "\n\n----\n'''Preview message''': Transclusion count updated automatically ([[Template:High-use/doc#Technical details|see documentation]])." or '' if count == nil then if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end end local title = mw.title.getCurrentTitle() if title.subpageText == "doc" or title.subpageText == "sandbox" then title = title.basePageTitle end local systemMessages = frame.args['system'] if frame.args['system'] == '' then systemMessages = nil end -- This retrieves the project URL automatically to simplify localiation. local templateCount = ('on [https://linkcount.toolforge.org/index.php?project=%s&page=%s %s pages]'):format( mw.title.getCurrentTitle():fullUrl():gsub('//(.-)/.*', '%1'), mw.uri.encode(title.fullText), p.num(frame, count)) local used_on_text = "'''This " .. (mw.title.getCurrentTitle().namespace == 828 and "Lua module" or "template") .. ' is used '; if systemMessages then used_on_text = used_on_text .. systemMessages .. ((count and count > 2000) and ("''', and " .. templateCount) or ("'''")) else used_on_text = used_on_text .. templateCount .. "'''" end local sandbox_text = ("%s's [[%s/sandbox|/sandbox]] or [[%s/testcases|/testcases]] subpages, or in your own [[%s]]. "):format( (mw.title.getCurrentTitle().namespace == 828 and "module" or "template"), title.fullText, title.fullText, mw.title.getCurrentTitle().namespace == 828 and "Module:Sandbox|module sandbox" or "Wikipedia:User pages#SUB|user subpage" ) local infoArg = frame.args["info"] ~= "" and frame.args["info"] if (systemMessages or frame.args[1] == "risk" or (count and count >= 100000) ) then local info = systemMessages and '.<br/>Changes to it can cause immediate changes to the Wikipedia user interface.' or '.' if infoArg then info = info .. "<br />" .. infoArg end sandbox_text = info .. '<br /> To avoid major disruption' .. (count and count >= 100000 and ' and server load' or '') .. ', any changes should be tested in the ' .. sandbox_text .. 'The tested changes can be added to this page in a single edit. ' else sandbox_text = (infoArg and ('.<br />' .. infoArg .. ' C') or ' and c') .. 'hanges may be widely noticed. Test changes in the ' .. sandbox_text end local discussion_text = systemMessages and 'Please discuss changes ' or 'Consider discussing changes ' if frame.args["2"] and frame.args["2"] ~= "" and frame.args["2"] ~= "yes" then discussion_text = string.format("%sat [[%s]]", discussion_text, frame.args["2"]) else discussion_text = string.format("%son the [[%s|talk page]]", discussion_text, title.talkPageTitle.fullText ) end return used_on_text .. sandbox_text .. discussion_text .. " before implementing them." .. bot_text end function p.main(frame) local count = nil if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end local image = "[[File:Ambox warning yellow.svg|40px|alt=Warning|link=]]" local type_param = "style" local epilogue = '' if frame.args['system'] and frame.args['system'] ~= '' then image = "[[File:Ambox important.svg|40px|alt=Warning|link=]]" type_param = "content" local nocat = frame:getParent().args['nocat'] or frame.args['nocat'] local categorise = (nocat == '' or not yesno(nocat)) if categorise then epilogue = frame:preprocess('{{Sandbox other||{{#switch:{{#invoke:Effective protection level|{{#switch:{{NAMESPACE}}|File=upload|#default=edit}}|{{FULLPAGENAME}}}}|sysop|templateeditor|interfaceadmin=|#default=[[Category:Pages used in system messages needing protection]]}}}}') end elseif (frame.args[1] == "risk" or (count and count >= 100000)) then image = "[[File:Ambox warning orange.svg|40px|alt=Warning|link=]]" type_param = "content" end if frame.args["form"] == "editnotice" then return frame:expandTemplate{ title = 'editnotice', args = { ["image"] = image, ["text"] = p.text(frame, count), ["expiry"] = (frame.args["expiry"] or "") } } .. epilogue else return require('Module:Message box').main('ombox', { type = type_param, image = image, text = p.text(frame, count), expiry = (frame.args["expiry"] or "") }) .. epilogue end end return p 134551888e066954a89c109d2faa8af71a4454a4 Module:Transclusion count 828 365 724 2023-05-30T20:51:38Z wikipedia>Isabelle Belato 0 Changed protection settings for "[[Module:Transclusion count]]": [[WP:High-risk templates|Highly visible template]]; requested at [[WP:RfPP]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) Scribunto text/plain local p = {} function p.fetch(frame) local template = nil local return_value = nil -- Use demo parameter if it exists, otherswise use current template name local namespace = mw.title.getCurrentTitle().namespace if frame.args["demo"] and frame.args["demo"] ~= "" then template = mw.ustring.gsub(frame.args["demo"],"^[Tt]emplate:","") elseif namespace == 10 then -- Template namespace template = mw.title.getCurrentTitle().text elseif namespace == 828 then -- Module namespace template = (mw.site.namespaces[828].name .. ":" .. mw.title.getCurrentTitle().text) end -- If in template or module namespace, look up count in /data if template ~= nil then namespace = mw.title.new(template, "Template").namespace if namespace == 10 or namespace == 828 then template = mw.ustring.gsub(template, "/doc$", "") -- strip /doc from end template = mw.ustring.gsub(template, "/sandbox$", "") -- strip /sandbox from end local index = mw.ustring.sub(mw.title.new(template).text,1,1) local status, data = pcall(function () return(mw.loadData('Module:Transclusion_count/data/' .. (mw.ustring.find(index, "%a") and index or "other"))) end) if status then return_value = tonumber(data[mw.ustring.gsub(template, " ", "_")]) end end end -- If database value doesn't exist, use value passed to template if return_value == nil and frame.args[1] ~= nil then local arg1=mw.ustring.match(frame.args[1], '[%d,]+') if arg1 and arg1 ~= '' then return_value = tonumber(frame:callParserFunction('formatnum', arg1, 'R')) end end return return_value end -- Tabulate this data for [[Wikipedia:Database reports/Templates transcluded on the most pages]] function p.tabulate(frame) local list = {} for i = 65, 91 do local data = mw.loadData('Module:Transclusion count/data/' .. ((i == 91) and 'other' or string.char(i))) for name, count in pairs(data) do table.insert(list, {mw.title.new(name, "Template").fullText, count}) end end table.sort(list, function(a, b) return (a[2] == b[2]) and (a[1] < b[1]) or (a[2] > b[2]) end) local lang = mw.getContentLanguage(); for i = 1, #list do list[i] = ('|-\n| %d || [[%s]] || %s\n'):format(i, list[i][1]:gsub('_', ' '), lang:formatNum(list[i][2])) end return table.concat(list) end return p 000ef6bcbf7b66e727870b0c300c4009da300513 Module:Video game reviews/data 828 470 940 2023-06-13T17:57:12Z wikipedia>Ferret 0 Per [[Special:Diff/1159965884]], add OpenCritic Scribunto text/plain local reviewers = { { "''[[1Up.com]]''", '1UP' }, { "''[[4Players]]''", '4P' }, { "''[[ACE (magazine)|ACE]]''", 'ACE' }, { "''[[Adventure Gamers]]''", 'AdvGamers' }, { "''[[AllGame]]''", 'Allgame' }, { "''[[Aktueller Software Markt]]''", 'ASM' }, { "''[[Amiga Action]]''", 'AmAction' }, { "''[[Amiga Computing]]''", 'AmComputing' }, { "''[[Amiga Force]]''", 'AmForce' }, { "''[[Amiga Format]]''", 'AmFormat' }, { "''[[Amiga Power]]''", 'AmPower' }, { "''[[Amiga User International]]''", 'AmUI' }, { "''[[Amstrad Action]]''", 'AAction' }, { "''[[Amtix]]''", 'Amtix' }, { "''[[The A.V. Club]]''", 'AVC' }, { "''[[Gemaga|Beep! MegaDrive]]''", 'BMD' }, { "''[[CNET Gamecenter]]''", 'CNG' }, { "''[[Computer Game Review]]''", 'CGR' }, { "''[[Computer Games Magazine]]''", 'CGM' }, { "''[[Computer Games Magazine|Computer Games Strategy Plus]]''", 'CGSP' }, { "''[[Computer Gaming World]]''", 'CGW' }, { "''Consoles +''", 'CP' }, { "''[[Crash (magazine)|Crash]]''", 'CRASH' }, { "''[[Computer and Video Games]]''", 'CVG' }, { "''[[Destructoid]]''", 'Destruct' }, { "''[[Digital Trends]]''", 'DT' }, { "''[[Dragon (magazine)|Dragon]]''", 'Dragon' }, { "''[[Easy Allies]]''", 'EZA' }, { "''[[Edge (magazine)|Edge]]''", 'Edge' }, { "''[[Electronic Gaming Monthly]]''", 'EGM' }, { "''[[EP Daily]]''", 'EPD' }, { "''[[Eurogamer]]''", 'EuroG' }, { "''[[Famitsu]]''", 'Fam' }, { "''[[G4 (American TV network)|G4]]''", 'G4' }, { "''[[Game Informer]]''", 'GI' }, { "''[[Game Players]]''", 'GP' }, { "''[[GameDaily]]''", 'GD' }, { "''[[GameFan]]''", 'GameFan' }, { "''[[Gamekult]]''", 'Gamekult' }, { "''[[GamePro]]''", 'GamePro' }, { "''[[GameRevolution]]''", 'GameRev' }, { "''[[GamesMaster (magazine)|GamesMaster]]''", 'GMaster' }, { "''[[GameSpot]]''", 'GSpot' }, { "''[[GameSpy]]''", 'GSpy' }, { "''[[GamesRadar+]]''", 'GRadar' }, { "''[[GameStar]]''", 'GStar' }, { "''[[GamesTM]]''", 'GTM' }, { "''[[Games-X]]''", 'GX' }, { "''[[GameTrailers]]''", 'GT' }, { "''[[Gamezebo]]''", 'Gamezebo' }, { "''GameZone''", 'GameZone' }, { "''Gekkan PC Engine''", 'GPCE' }, { "''Génération 4''", 'Gen4' }, { "''[[Giant Bomb]]''", 'GB' }, { "''Hardcore Gamer''", 'HCG' }, { "''[[HobbyConsolas]]''", 'HC' }, { "''[[Hyper (magazine)|Hyper]]''", 'Hyper' }, { "''[[IGN]]''", 'IGN' }, { "''Impress Watch''", 'IW' }, { "''[[Jeuxvideo.com]]''", 'JXV' }, { "''Joypad''", 'JP' }, { "''[[Joystick (magazine)|Joystick]]''", 'JS' }, { "''[[Joystiq]]''", 'Joystiq' }, { "''[[Kill Screen]]''", 'KS' }, { "''M! Games''", 'MG' }, { "''[[MacLife]]''", 'ML' }, { "''[[Macworld]]''", 'MW' }, { "''Marukatsu PC Engine''", 'MPCE' }, { "''[[Maximum PC]]''", 'MaxPC' }, { "''[[Mean Machines Sega]]''", 'MMS' }, { "''Mega Fun''", 'MF' }, { "''MeriStation''", 'MS' }, { "''[[Micromanía]]''", 'MIC' }, { "''[[NGC Magazine|N64 Magazine]]''", 'N64' }, { "''[[Next Generation (magazine)|Next Generation]]''", 'NGen' }, { "''[[Nintendo Gamer|NGamer]]''", 'NG' }, { "''[[NGC Magazine]]''", 'NGC' }, { "''[[Nintendo Life]]''", 'NLife' }, { "''[[Nintendo Power]]''", 'NP' }, { "''Nintendo World Report''", 'NWR' }, { "''[[NME]]''", 'NME' }, { "''[[Official Nintendo Magazine]]''", 'ONM' }, { "''[[PlayStation Official Magazine – Australia]]''", 'OPMAU' }, { "''[[PlayStation Official Magazine – UK]]''", 'OPMUK' }, { "''[[Official U.S. PlayStation Magazine]]''", 'OPM' }, { "[[Official Xbox Magazine|''Official Xbox Magazine'' (UK)]]", 'OXMUK' }, { "[[Official Xbox Magazine|''Official Xbox Magazine'' (US)]]", 'OXM' }, { "''PALGN''", 'PALGN' }, { "''[[PC Accelerator]]''", 'PCA' }, { "''PC Engine Fan''", 'PCEF' }, { "''[[PC Format]]''", 'PCF' }, { "[[PC Gamer|''PC Gamer'' (UK)]]", 'PCGUK' }, { "[[PC Gamer|''PC Gamer'' (US)]]", 'PCGUS' }, { "[[PC Games|''PC Games'' (DE)]]", 'PCG' }, { "[[GamePro#PC Games|''PC Games'' (US)]]", 'GPPCG' }, { "''[[PC PowerPlay]]''", 'PCPP' }, { "''[[PC Zone]]''", 'PCZone' }, { "''[[PCGamesN]]''", 'PCGN' }, { "''[[PCMag]]''", 'PCM' }, { "''[[Play (UK magazine)|Play]]''", 'Play' }, { "''[[Pocket Gamer]]''", 'PG' }, { "''[[Polygon (website)|Polygon]]''", 'Poly' }, { "''Player One''", 'PO' }, { "''[[PlayStation: The Official Magazine]]''", 'PSM' }, { "''[[PSM3]]''", 'PSM3' }, { "''[[Push Square]]''", 'PSQ' }, { "''[[Newsfield#Raze|Raze]]''", 'Raze' }, { "''[[Retro Gamer]]''", 'Retro' }, { "''RPGamer''", 'RPG' }, { "''RPGFan''", 'RPGFan' }, { "''[[Shacknews]]''", 'SN' }, { "''[[Sinclair User]]''", 'SUser' }, { "''[[ST Action]]''", 'STAction' }, { "''[[ST Format]]''", 'STFormat' }, { "''[[ST Review]]''", 'STRev' }, { "''Superjuegos''", 'SJ' }, { "''Super Game Power''", 'SGP' }, { "''[[Super Play]]''", 'SP' }, { "''[[TeamXbox]]''", 'TX' }, { "''[[TechRadar]]''", 'TR' }, { "''[[The Daily Telegraph|The Telegraph]]''", 'TELE' }, { "[[The Games Machine|''The Games Machine'' (UK)]]", 'TGM' }, { "[[The Games Machine (Italy)|''The Games Machine'' (Italy)]]", 'TGMIt' }, { "''[[The Guardian]]''", 'TG' }, { "''[[Tilt (French magazine)|Tilt]]''", 'TILT' }, { "''[[Total!]]''", 'TOT' }, { "''[[TouchArcade]]''", 'TA' }, { "''[[USgamer]]''", 'USG' }, { "''[[VentureBeat]]''", 'VB' }, { "''[[Video Games Chronicle]]''", 'VGC' }, { "''[[VG247]]''", 'VG247' }, { "''Video Games'' (DE)", 'VGS' }, { "''VideoGamer.com''", 'VG' }, { "''[[VideoGames & Computer Entertainment]]''", 'VGCE' }, { "''[[X-Play]]''", 'XPlay' }, { "''[[Your Sinclair]]''", 'YSinclair' }, { "''[[Zero (video game magazine)|Zero]]''", 'Zero' }, { "''[[Zzap!64]]''", 'Z64' }, } local aggregators = { { '[[GameRankings]]', 'GR' }, { '[[Metacritic]]', 'MC' }, { '[[OpenCritic]]', 'OC' }, } local systems = { { '[[3DO Interactive Multiplayer|3DO]]', '3DO' }, { '[[Nintendo 3DS|3DS]]', '3DS' }, { '[[Amiga]]', 'AMI' }, { '[[Arcade game|Arcade]]', 'ARC' }, { '[[Atari 2600]]', 'A2600' }, { '[[Atari Jaguar]]', 'JAG' }, { '[[Atari Lynx]]', 'LYNX' }, { '[[Atari ST]]', 'AST' }, { '[[Commodore 64|C64]]', 'C64' }, { '[[Amiga CD32|CD32]]', 'CD32' }, { '[[ColecoVision]]', 'CV' }, { '[[MS-DOS|DOS]]', 'DOS' }, { '[[Dreamcast]]', 'SDC' }, { '[[Nintendo DS|DS]]', 'DS' }, { '[[Game Boy]]', 'GB' }, { '[[Game Boy Advance|GBA]]', 'GBA' }, { '[[Game Boy Color|GBC]]', 'GBC' }, { '[[GameCube|GC]]', 'NGC' }, { '[[List of video game consoles|General]]', 'GEN' }, { '[[Intellivision]]', 'INT' }, { '[[iOS]]', 'iOS' }, { '[[Macintosh]]', 'MAC' }, { '[[Master System]]', 'SMS' }, { '[[Mobile phone|mobile]]', 'MOB' }, { '[[N-Gage (device)|N-Gage]]', 'N-G' }, { '[[Nintendo 64|N64]]', 'N64' }, { '[[Nintendo Entertainment System|NES]]', 'NES' }, { '[[Nintendo Switch|NS]]', 'NS' }, { '[[Personal computer|PC]]', 'PC' }, { '[[PlayStation Vita|PS Vita]]', 'VITA' }, { '[[PlayStation (console)|PS]]', 'PS' }, { '[[PlayStation 2|PS2]]', 'PS2' }, { '[[PlayStation 3|PS3]]', 'PS3' }, { '[[PlayStation 4|PS4]]', 'PS4' }, { '[[PlayStation 5|PS5]]', 'PS5' }, { '[[PlayStation Portable|PSP]]', 'PSP' }, { '[[Sega Saturn|Saturn]]', 'SSAT' }, { '[[Sega Genesis]]', 'SMD' }, { '[[Sega Game Gear|SGG]]', 'SGG' }, { '[[Nvidia Shield|Shield]]', 'NSHI' }, { '[[Super Nintendo Entertainment System|SNES]]', 'SNES' }, { '[[TurboGrafx-16]]', 'TG16' }, { '[[Wii]]', 'WII' }, { '[[Wii U]]', 'WIIU' }, { '[[Xbox (console)|Xbox]]', 'XBOX' }, { '[[Xbox 360]]', 'X360' }, { '[[Xbox One]]', 'XONE' }, { '[[Xbox Series X and Series S|Xbox Series X/S]]', 'XSXS' }, { '[[ZX Spectrum|ZX]]', 'ZX' }, } local function setupSortkeys(t) local retval = {} for k, v in ipairs(t) do retval[v[2]] = { name = v[1], sortkey = k } end return retval end return { reviewers = setupSortkeys(reviewers), aggregators = setupSortkeys(aggregators), systems = setupSortkeys(systems), i18n = { wrapper = 'Template:Video game reviews', templatestyles = 'Module:Video game reviews/styles.css', pattern = { reviewer = '^rev%d+$', aggregator = '^agg%d+$', award = '^award%d+$', }, class = { aggregators = 'vgr-aggregators', awards = 'vgr-awards', centeredpub = 'vgr-center', container = 'video-game-reviews', containerleft = 'vgr-left', containernone = 'vgr-none', containersingle = 'vgr-single', headerrow = 'vgr-hrow', na = 'table-na', -- same as Template:n/a reviews = 'vgr-reviews', stacked = 'vgr-stacked', subtitle = 'vgr-subtitle', title = 'vgr-title', wikidata = 'vgr-edit-on-wikidata', -- we keep wikitable around even though all the styles are overriden -- because it helps some scripts to know to look for a real data table wikitable = 'wikitable', }, state = { autocollapse = 'autocollapse', -- you may not have this on your wiki collapsed = 'collapsed', expanded = 'expanded', }, align = { left = 'left', none = 'none', }, category = { empty = '[[Category:Empty templates on articles]]', multiplatform = '[[Category:Articles using Video game reviews template in multiple platform mode]]', singleplatform = '[[Category:Articles using Video game reviews template in single platform mode]]', }, display = { aggregateScore = 'Aggregate score', aggregateScores = 'Aggregate scores', aggregator = 'Aggregator', award = 'Award', awards = 'Awards', na = 'N/A', publication = 'Publication', reception = 'Reception', reviewScore = 'Review score', reviewScores = 'Review scores', score = 'Score', }, }, argi18n = { -- For non-English wikis, add translations of argument keys here. -- Example: -- subtitle = 'untertitel' }, } 83ed24f7e9467631a402d0d410b9e45b579afad3 Module:WikidataIB 828 447 894 2023-06-15T20:52:11Z wikipedia>Uzume 0 strict Scribunto text/plain -- Version: 2021-02-06 -- Module to implement use of a blacklist and whitelist for infobox fields -- Can take a named parameter |qid which is the Wikidata ID for the article -- if not supplied, it will use the Wikidata ID associated with the current page. -- Fields in blacklist are never to be displayed, i.e. module must return nil in all circumstances -- Fields in whitelist return local value if it exists or the Wikidata value otherwise -- The name of the field that this function is called from is passed in named parameter |name -- The name is compulsory when blacklist or whitelist is used, -- so the module returns nil if it is not supplied. -- blacklist is passed in named parameter |suppressfields (or |spf) -- whitelist is passed in named parameter |fetchwikidata (or |fwd) require("strict") local p = {} local cdate -- initialise as nil and only load _complex_date function if needed -- Module:Complex date is loaded lazily and has the following dependencies: -- Module:Calendar -- Module:ISOdate -- Module:DateI18n -- Module:I18n/complex date -- Module:Ordinal -- Module:I18n/ordinal -- Module:Yesno -- Module:Formatnum -- Module:Linguistic -- -- The following, taken from https://www.mediawiki.org/wiki/Wikibase/DataModel#Dates_and_times, -- is needed to use Module:Complex date which seemingly requires date precision as a string. -- It would work better if only the authors of the mediawiki page could spell 'millennium'. local dp = { [6] = "millennium", [7] = "century", [8] = "decade", [9] = "year", [10] = "month", [11] = "day", } local i18n = { ["errors"] = { ["property-not-found"] = "Property not found.", ["No property supplied"] = "No property supplied", ["entity-not-found"] = "Wikidata entity not found.", ["unknown-claim-type"] = "Unknown claim type.", ["unknown-entity-type"] = "Unknown entity type.", ["qualifier-not-found"] = "Qualifier not found.", ["site-not-found"] = "Wikimedia project not found.", ["labels-not-found"] = "No labels found.", ["descriptions-not-found"] = "No descriptions found.", ["aliases-not-found"] = "No aliases found.", ["unknown-datetime-format"] = "Unknown datetime format.", ["local-article-not-found"] = "Article is available on Wikidata, but not on Wikipedia", ["dab-page"] = " (dab)", }, ["months"] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }, ["century"] = "century", ["BC"] = "BC", ["BCE"] = "BCE", ["ordinal"] = { [1] = "st", [2] = "nd", [3] = "rd", ["default"] = "th" }, ["filespace"] = "File", ["Unknown"] = "Unknown", ["NaN"] = "Not a number", -- set the following to the name of a tracking category, -- e.g. "[[Category:Articles with missing Wikidata information]]", or "" to disable: ["missinginfocat"] = "[[Category:Articles with missing Wikidata information]]", ["editonwikidata"] = "Edit this on Wikidata", ["latestdatequalifier"] = function (date) return "before " .. date end, -- some languages, e.g. Bosnian use a period as a suffix after each number in a date ["datenumbersuffix"] = "", ["list separator"] = ", ", ["multipliers"] = { [0] = "", [3] = " thousand", [6] = " million", [9] = " billion", [12] = " trillion", } } -- This allows an internationisation module to override the above table if 'en' ~= mw.getContentLanguage():getCode() then require("Module:i18n").loadI18n("Module:WikidataIB/i18n", i18n) end -- This piece of html implements a collapsible container. Check the classes exist on your wiki. local collapsediv = '<div class="mw-collapsible mw-collapsed" style="width:100%; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}">' -- Some items should not be linked. -- Each wiki can create a list of those in Module:WikidataIB/nolinks -- It should return a table called itemsindex, containing true for each item not to be linked local donotlink = {} local nolinks_exists, nolinks = pcall(mw.loadData, "Module:WikidataIB/nolinks") if nolinks_exists then donotlink = nolinks.itemsindex end -- To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised, and others are quoted. -- The submodule [[Module:WikidataIB/titleformats]] lists the entity-ids used in 'instance of' (P31), -- which allows this module to identify the values that should be formatted. -- WikidataIB/titleformats exports a table p.formats, which is indexed by entity-id, and contains the value " or '' local formats = {} local titleformats_exists, titleformats = pcall(mw.loadData, "Module:WikidataIB/titleformats") if titleformats_exists then formats = titleformats.formats end ------------------------------------------------------------------------------- -- Private functions ------------------------------------------------------------------------------- -- ------------------------------------------------------------------------------- -- makeOrdinal needs to be internationalised along with the above: -- takes cardinal number as a numeric and returns the ordinal as a string -- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local makeOrdinal = function(cardinal) local ordsuffix = i18n.ordinal.default if cardinal % 10 == 1 then ordsuffix = i18n.ordinal[1] elseif cardinal % 10 == 2 then ordsuffix = i18n.ordinal[2] elseif cardinal % 10 == 3 then ordsuffix = i18n.ordinal[3] end -- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th' -- similarly for 12 and 13, etc. if (cardinal % 100 == 11) or (cardinal % 100 == 12) or (cardinal % 100 == 13) then ordsuffix = i18n.ordinal.default end return tostring(cardinal) .. ordsuffix end ------------------------------------------------------------------------------- -- findLang takes a "langcode" parameter if supplied and valid -- otherwise it tries to create it from the user's set language ({{int:lang}}) -- failing that it uses the wiki's content language. -- It returns a language object ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local findLang = function(langcode) local langobj langcode = mw.text.trim(langcode or "") if mw.language.isKnownLanguageTag(langcode) then langobj = mw.language.new( langcode ) else langcode = mw.getCurrentFrame():callParserFunction('int', {'lang'}) if mw.language.isKnownLanguageTag(langcode) then langobj = mw.language.new( langcode ) else langobj = mw.language.getContentLanguage() end end return langobj end ------------------------------------------------------------------------------- -- _getItemLangCode takes a qid parameter (using the current page's qid if blank) -- If the item for that qid has property country (P17) it looks at the first preferred value -- If the country has an official language (P37), it looks at the first preferred value -- If that official language has a language code (P424), it returns the first preferred value -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local _getItemLangCode = function(qid) qid = mw.text.trim(qid or ""):upper() if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return end local prop17 = mw.wikibase.getBestStatements(qid, "P17")[1] if not prop17 or prop17.mainsnak.snaktype ~= "value" then return end local qid17 = prop17.mainsnak.datavalue.value.id local prop37 = mw.wikibase.getBestStatements(qid17, "P37")[1] if not prop37 or prop37.mainsnak.snaktype ~= "value" then return end local qid37 = prop37.mainsnak.datavalue.value.id local prop424 = mw.wikibase.getBestStatements(qid37, "P424")[1] if not prop424 or prop424.mainsnak.snaktype ~= "value" then return end return prop424.mainsnak.datavalue.value end ------------------------------------------------------------------------------- -- roundto takes a number (x) -- and returns it rounded to (sf) significant figures ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local roundto = function(x, sf) if x == 0 then return 0 end local s = 1 if x < 0 then x = -x s = -1 end if sf < 1 then sf = 1 end local p = 10 ^ (math.floor(math.log10(x)) - sf + 1) x = math.floor(x / p + 0.5) * p * s -- if it's integral, cast to an integer: if x == math.floor(x) then x = math.floor(x) end return x end ------------------------------------------------------------------------------- -- decimalToDMS takes a decimal degrees (x) with precision (p) -- and returns degrees/minutes/seconds according to the precision ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local decimalToDMS = function(x, p) -- if p is not supplied, use a precision around 0.1 seconds if not tonumber(p) then p = 1e-4 end local d = math.floor(x) local ms = (x - d) * 60 if p > 0.5 then -- precision is > 1/2 a degree if ms > 30 then d = d + 1 end ms = 0 end local m = math.floor(ms) local s = (ms - m) * 60 if p > 0.008 then -- precision is > 1/2 a minute if s > 30 then m = m +1 end s = 0 elseif p > 0.00014 then -- precision is > 1/2 a second s = math.floor(s + 0.5) elseif p > 0.000014 then -- precision is > 1/20 second s = math.floor(10 * s + 0.5) / 10 elseif p > 0.0000014 then -- precision is > 1/200 second s = math.floor(100 * s + 0.5) / 100 else -- cap it at 3 dec places for now s = math.floor(1000 * s + 0.5) / 1000 end return d, m, s end ------------------------------------------------------------------------------- -- decimalPrecision takes a decimal (x) with precision (p) -- and returns x rounded approximately to the given precision -- precision should be between 1 and 1e-6, preferably a power of 10. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local decimalPrecision = function(x, p) local s = 1 if x < 0 then x = -x s = -1 end -- if p is not supplied, pick an arbitrary precision if not tonumber(p) then p = 1e-4 elseif p > 1 then p = 1 elseif p < 1e-6 then p = 1e-6 else p = 10 ^ math.floor(math.log10(p)) end x = math.floor(x / p + 0.5) * p * s -- if it's integral, cast to an integer: if x == math.floor(x) then x = math.floor(x) end -- if it's less than 1e-4, it will be in exponent form, so return a string with 6dp -- 9e-5 becomes 0.000090 if math.abs(x) < 1e-4 then x = string.format("%f", x) end return x end ------------------------------------------------------------------------------- -- formatDate takes a datetime of the usual format from mw.wikibase.entity:formatPropertyValues -- like "1 August 30 BCE" as parameter 1 -- and formats it according to the df (date format) and bc parameters -- df = ["dmy" / "mdy" / "y"] default will be "dmy" -- bc = ["BC" / "BCE"] default will be "BCE" ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local format_Date = function(datetime, dateformat, bc) local datetime = datetime or "1 August 30 BCE" -- in case of nil value -- chop off multiple vales and/or any hours, mins, etc. -- keep anything before punctuation - we just want a single date: local dateval = string.match( datetime, "[%w ]+") local dateformat = string.lower(dateformat or "dmy") -- default to dmy local bc = string.upper(bc or "") -- can't use nil for bc -- we only want to accept two possibilities: BC or default to BCE if bc == "BC" then bc = "&nbsp;" .. i18n["BC"] -- prepend a non-breaking space. else bc = "&nbsp;" .. i18n["BCE"] end local postchrist = true -- start by assuming no BCE local dateparts = {} for word in string.gmatch(dateval, "%w+") do if word == "BCE" or word == "BC" then -- *** internationalise later *** postchrist = false else -- we'll keep the parts that are not 'BCE' in a table dateparts[#dateparts + 1] = word end end if postchrist then bc = "" end -- set AD dates to no suffix *** internationalise later *** local sep = "&nbsp;" -- separator is nbsp local fdate = table.concat(dateparts, sep) -- set formatted date to same order as input -- if we have day month year, check dateformat if #dateparts == 3 then if dateformat == "y" then fdate = dateparts[3] elseif dateformat == "mdy" then fdate = dateparts[2] .. sep .. dateparts[1] .. "," .. sep .. dateparts[3] end elseif #dateparts == 2 and dateformat == "y" then fdate = dateparts[2] end return fdate .. bc end ------------------------------------------------------------------------------- -- dateFormat is the handler for properties that are of type "time" -- It takes timestamp, precision (6 to 11 per mediawiki), dateformat (y/dmy/mdy), BC format (BC/BCE), -- a plaindate switch (yes/no/adj) to en/disable "sourcing circumstances"/use adjectival form, -- any qualifiers for the property, the language, and any adjective to use like 'before'. -- It passes the date through the "complex date" function -- and returns a string with the internatonalised date formatted according to preferences. ------------------------------------------------------------------------------- -- Dependencies: findLang(); cdate(); dp[] ------------------------------------------------------------------------------- local dateFormat = function(timestamp, dprec, df, bcf, pd, qualifiers, lang, adj, model) -- output formatting according to preferences (y/dmy/mdy/ymd) df = (df or ""):lower() -- if ymd is required, return the part of the timestamp in YYYY-MM-DD form -- but apply Year zero#Astronomers fix: 1 BC = 0000; 2 BC = -0001; etc. if df == "ymd" then if timestamp:sub(1,1) == "+" then return timestamp:sub(2,11) else local yr = tonumber(timestamp:sub(2,5)) - 1 yr = ("000" .. yr):sub(-4) if yr ~= "0000" then yr = "-" .. yr end return yr .. timestamp:sub(6,11) end end -- A year can be stored like this: "+1872-00-00T00:00:00Z", -- which is processed here as if it were the day before "+1872-01-01T00:00:00Z", -- and that's the last day of 1871, so the year is wrong. -- So fix the month 0, day 0 timestamp to become 1 January instead: timestamp = timestamp:gsub("%-00%-00T", "-01-01T") -- just in case date precision is missing dprec = dprec or 11 -- override more precise dates if required dateformat is year alone: if df == "y" and dprec > 9 then dprec = 9 end -- complex date only deals with precisions from 6 to 11, so clip range dprec = dprec>11 and 11 or dprec dprec = dprec<6 and 6 or dprec -- BC format is "BC" or "BCE" bcf = (bcf or ""):upper() -- plaindate only needs the first letter (y/n/a) pd = (pd or ""):sub(1,1):lower() if pd == "" or pd == "n" or pd == "f" or pd == "0" then pd = false end -- in case language isn't passed lang = lang or findLang().code -- set adj as empty if nil adj = adj or "" -- extract the day, month, year from the timestamp local bc = timestamp:sub(1, 1)=="-" and "BC" or "" local year, month, day = timestamp:match("[+-](%d*)-(%d*)-(%d*)T") local iso = tonumber(year) -- if year is missing, let it throw an error -- this will adjust the date format to be compatible with cdate -- possible formats are Y, YY, YYY0, YYYY, YYYY-MM, YYYY-MM-DD if dprec == 6 then iso = math.floor( (iso - 1) / 1000 ) + 1 end if dprec == 7 then iso = math.floor( (iso - 1) / 100 ) + 1 end if dprec == 8 then iso = math.floor( iso / 10 ) .. "0" end if dprec == 10 then iso = year .. "-" .. month end if dprec == 11 then iso = year .. "-" .. month .. "-" .. day end -- add "circa" (Q5727902) from "sourcing circumstances" (P1480) local sc = not pd and qualifiers and qualifiers.P1480 if sc then for k1, v1 in pairs(sc) do if v1.datavalue and v1.datavalue.value.id == "Q5727902" then adj = "circa" break end end end -- deal with Julian dates: -- no point in saying that dates before 1582 are Julian - they are by default -- doesn't make sense for dates less precise than year -- we can suppress it by setting |plaindate, e.g. for use in constructing categories. local calendarmodel = "" if tonumber(year) > 1582 and dprec > 8 and not pd and model == "http://www.wikidata.org/entity/Q1985786" then calendarmodel = "julian" end if not cdate then cdate = require("Module:Complex date")._complex_date end local fdate = cdate(calendarmodel, adj, tostring(iso), dp[dprec], bc, "", "", "", "", lang, 1) -- this may have QuickStatements info appended to it in a div, so remove that fdate = fdate:gsub(' <div style="display: none;">[^<]*</div>', '') -- it may also be returned wrapped in a microformat, so remove that fdate = fdate:gsub("<[^>]*>", "") -- there may be leading zeros that we should remove fdate = fdate:gsub("^0*", "") -- if a plain date is required, then remove any links (like BC linked) if pd then fdate = fdate:gsub("%[%[.*|", ""):gsub("]]", "") end -- if 'circa', use the abbreviated form *** internationalise later *** fdate = fdate:gsub('circa ', '<abbr title="circa">c.</abbr>&nbsp;') -- deal with BC/BCE if bcf == "BCE" then fdate = fdate:gsub('BC', 'BCE') end -- deal with mdy format if df == "mdy" then fdate = fdate:gsub("(%d+) (%w+) (%d+)", "%2 %1, %3") end -- deal with adjectival form *** internationalise later *** if pd == "a" then fdate = fdate:gsub(' century', '-century') end return fdate end ------------------------------------------------------------------------------- -- parseParam takes a (string) parameter, e.g. from the list of frame arguments, -- and makes "false", "no", and "0" into the (boolean) false -- it makes the empty string and nil into the (boolean) value passed as default -- allowing the parameter to be true or false by default. -- It returns a boolean. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local parseParam = function(param, default) if type(param) == "boolean" then param = tostring(param) end if param and param ~= "" then param = param:lower() if (param == "false") or (param:sub(1,1) == "n") or (param == "0") then return false else return true end else return default end end ------------------------------------------------------------------------------- -- _getSitelink takes the qid of a Wikidata entity passed as |qid= -- It takes an optional parameter |wiki= to determine which wiki is to be checked for a sitelink -- If the parameter is blank, then it uses the local wiki. -- If there is a sitelink to an article available, it returns the plain text link to the article -- If there is no sitelink, it returns nil. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local _getSitelink = function(qid, wiki) qid = (qid or ""):upper() if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end wiki = wiki or "" local sitelink if wiki == "" then sitelink = mw.wikibase.getSitelink(qid) else sitelink = mw.wikibase.getSitelink(qid, wiki) end return sitelink end ------------------------------------------------------------------------------- -- _getCommonslink takes an optional qid of a Wikidata entity passed as |qid= -- It returns one of the following in order of preference: -- the Commons sitelink of the Wikidata entity - but not if onlycat=true and it's not a category; -- the Commons sitelink of the topic's main category of the Wikidata entity; -- the Commons category of the Wikidata entity - unless fallback=false. ------------------------------------------------------------------------------- -- Dependencies: _getSitelink(); parseParam() ------------------------------------------------------------------------------- local _getCommonslink = function(qid, onlycat, fallback) qid = (qid or ""):upper() if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end onlycat = parseParam(onlycat, false) if fallback == "" then fallback = nil end local sitelink = _getSitelink(qid, "commonswiki") if onlycat and sitelink and sitelink:sub(1,9) ~= "Category:" then sitelink = nil end if not sitelink then -- check for topic's main category local prop910 = mw.wikibase.getBestStatements(qid, "P910")[1] if prop910 then local tmcid = prop910.mainsnak.datavalue and prop910.mainsnak.datavalue.value.id sitelink = _getSitelink(tmcid, "commonswiki") end if not sitelink then -- check for list's main category local prop1754 = mw.wikibase.getBestStatements(qid, "P1754")[1] if prop1754 then local tmcid = prop1754.mainsnak.datavalue and prop1754.mainsnak.datavalue.value.id sitelink = _getSitelink(tmcid, "commonswiki") end end end if not sitelink and fallback then -- check for Commons category (string value) local prop373 = mw.wikibase.getBestStatements(qid, "P373")[1] if prop373 then sitelink = prop373.mainsnak.datavalue and prop373.mainsnak.datavalue.value if sitelink then sitelink = "Category:" .. sitelink end end end return sitelink end ------------------------------------------------------------------------------- -- The label in a Wikidata item is subject to vulnerabilities -- that an attacker might try to exploit. -- It needs to be 'sanitised' by removing any wikitext before use. -- If it doesn't exist, return the id for the item -- a second (boolean) value is also returned, value is true when the label exists ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local labelOrId = function(id, lang) if lang == "default" then lang = findLang().code end local label if lang then label = mw.wikibase.getLabelByLang(id, lang) else label = mw.wikibase.getLabel(id) end if label then return mw.text.nowiki(label), true else return id, false end end ------------------------------------------------------------------------------- -- linkedItem takes an entity-id and returns a string, linked if possible. -- This is the handler for "wikibase-item". Preferences: -- 1. Display linked disambiguated sitelink if it exists -- 2. Display linked label if it is a redirect -- 3. TBA: Display an inter-language link for the label if it exists other than in default language -- 4. Display unlinked label if it exists -- 5. Display entity-id for now to indicate a label could be provided -- dtxt is text to be used instead of label, or nil. -- shortname is boolean switch to use P1813 (short name) instead of label if true. -- lang is the current language code. -- uselbl is boolean switch to force display of the label instead of the sitelink (default: false) -- linkredir is boolean switch to allow linking to a redirect (default: false) -- formatvalue is boolean switch to allow formatting as italics or quoted (default: false) ------------------------------------------------------------------------------- -- Dependencies: labelOrId(); donotlink[] ------------------------------------------------------------------------------- local linkedItem = function(id, args) local lprefix = (args.lp or args.lprefix or args.linkprefix or ""):gsub('"', '') -- toughen against nil values passed local lpostfix = (args.lpostfix or ""):gsub('"', '') local prefix = (args.prefix or ""):gsub('"', '') local postfix = (args.postfix or ""):gsub('"', '') local dtxt = args.dtxt local shortname = args.shortname local lang = args.lang or "en" -- fallback to default if missing local uselbl = args.uselabel or args.uselbl uselbl = parseParam(uselbl, false) local linkredir = args.linkredir linkredir = parseParam(linkredir, false) local formatvalue = args.formatvalue or args.fv formatvalue = parseParam(formatvalue, false) -- see if item might need italics or quotes local fmt = "" if next(formats) and formatvalue then for k, v in ipairs( mw.wikibase.getBestStatements(id, "P31") ) do if v.mainsnak.datavalue and formats[v.mainsnak.datavalue.value.id] then fmt = formats[v.mainsnak.datavalue.value.id] break -- pick the first match end end end local disp local sitelink = mw.wikibase.getSitelink(id) local label, islabel if dtxt then label, islabel = dtxt, true elseif shortname then -- see if there is a shortname in our language, and set label to it for k, v in ipairs( mw.wikibase.getBestStatements(id, "P1813") ) do if v.mainsnak.datavalue.value.language == lang then label, islabel = v.mainsnak.datavalue.value.text, true break end -- test for language match end -- loop through values of short name -- if we have no label set, then there was no shortname available if not islabel then label, islabel = labelOrId(id) shortname = false end else label, islabel = labelOrId(id) end if mw.site.siteName ~= "Wikimedia Commons" then if sitelink then if not (dtxt or shortname) then -- if sitelink and label are the same except for case, no need to process further if sitelink:lower() ~= label:lower() then -- strip any namespace or dab from the sitelink local pos = sitelink:find(":") or 0 local slink = sitelink if pos > 0 then local pfx = sitelink:sub(1,pos-1) if mw.site.namespaces[pfx] then -- that prefix is a valid namespace, so remove it slink = sitelink:sub(pos+1) end end -- remove stuff after commas or inside parentheses - ie. dabs slink = slink:gsub("%s%(.+%)$", ""):gsub(",.+$", "") -- if uselbl is false, use sitelink instead of label if not uselbl then -- use slink as display, preserving label case - find("^%u") is true for 1st char uppercase if label:find("^%u") then label = slink:gsub("^(%l)", string.upper) else label = slink:gsub("^(%u)", string.lower) end end end end if donotlink[label] then disp = prefix .. fmt .. label .. fmt .. postfix else disp = "[[" .. lprefix .. sitelink .. lpostfix .. "|" .. prefix .. fmt .. label .. fmt .. postfix .. "]]" end elseif islabel then -- no sitelink, label exists, so check if a redirect with that title exists, if linkredir is true -- display plain label by default disp = prefix .. fmt .. label .. fmt .. postfix if linkredir then local artitle = mw.title.new(label, 0) -- only nil if label has invalid chars if not donotlink[label] and artitle and artitle.redirectTarget then -- there's a redirect with the same title as the label, so let's link to that disp = "[[".. lprefix .. label .. lpostfix .. "|" .. prefix .. fmt .. label .. fmt .. postfix .. "]]" end end -- test if article title exists as redirect on current Wiki else -- no sitelink and no label, so return whatever was returned from labelOrId for now -- add tracking category [[Category:Articles with missing Wikidata information]] -- for enwiki, just return the tracking category if mw.wikibase.getGlobalSiteId() == "enwiki" then disp = i18n.missinginfocat else disp = prefix .. label .. postfix .. i18n.missinginfocat end end else local ccat = mw.wikibase.getBestStatements(id, "P373")[1] if ccat and ccat.mainsnak.datavalue then ccat = ccat.mainsnak.datavalue.value disp = "[[" .. lprefix .. "Category:" .. ccat .. lpostfix .. "|" .. prefix .. label .. postfix .. "]]" elseif sitelink then -- this asumes that if a sitelink exists, then a label also exists disp = "[[" .. lprefix .. sitelink .. lpostfix .. "|" .. prefix .. label .. postfix .. "]]" else -- no sitelink and no Commons cat, so return label from labelOrId for now disp = prefix .. label .. postfix end end return disp end ------------------------------------------------------------------------------- -- sourced takes a table representing a statement that may or may not have references -- it looks for a reference sourced to something not containing the word "wikipedia" -- it returns a boolean = true if it finds a sourced reference. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local sourced = function(claim) if claim.references then for kr, vr in pairs(claim.references) do local ref = mw.wikibase.renderSnaks(vr.snaks) if not ref:find("Wiki") then return true end end end end ------------------------------------------------------------------------------- -- setRanks takes a flag (parameter passed) that requests the values to return -- "b[est]" returns preferred if available, otherwise normal -- "p[referred]" returns preferred -- "n[ormal]" returns normal -- "d[eprecated]" returns deprecated -- multiple values are allowed, e.g. "preferred normal" (which is the default) -- "best" will override the other flags, and set p and n ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local setRanks = function(rank) rank = (rank or ""):lower() -- if nothing passed, return preferred and normal -- if rank == "" then rank = "p n" end local ranks = {} for w in string.gmatch(rank, "%a+") do w = w:sub(1,1) if w == "b" or w == "p" or w == "n" or w == "d" then ranks[w] = true end end -- check if "best" is requested or no ranks requested; and if so, set preferred and normal if ranks.b or not next(ranks) then ranks.p = true ranks.n = true end return ranks end ------------------------------------------------------------------------------- -- parseInput processes the Q-id , the blacklist and the whitelist -- if an input parameter is supplied, it returns that and ends the call. -- it returns (1) either the qid or nil indicating whether or not the call should continue -- and (2) a table containing all of the statements for the propertyID and relevant Qid -- if "best" ranks are requested, it returns those instead of all non-deprecated ranks ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local parseInput = function(frame, input_parm, property_id) -- There may be a local parameter supplied, if it's blank, set it to nil input_parm = mw.text.trim(input_parm or "") if input_parm == "" then input_parm = nil end -- return nil if Wikidata is not available if not mw.wikibase then return false, input_parm end local args = frame.args -- can take a named parameter |qid which is the Wikidata ID for the article. -- if it's not supplied, use the id for the current page local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end -- if there's no Wikidata item for the current page return nil if not qid then return false, input_parm end -- The blacklist is passed in named parameter |suppressfields local blacklist = args.suppressfields or args.spf or "" -- The whitelist is passed in named parameter |fetchwikidata local whitelist = args.fetchwikidata or args.fwd or "" if whitelist == "" then whitelist = "NONE" end -- The name of the field that this function is called from is passed in named parameter |name local fieldname = args.name or "" if blacklist ~= "" then -- The name is compulsory when blacklist is used, so return nil if it is not supplied if fieldname == "" then return false, nil end -- If this field is on the blacklist, then return nil if blacklist:find(fieldname) then return false, nil end end -- If we got this far then we're not on the blacklist -- The blacklist overrides any locally supplied parameter as well -- If a non-blank input parameter was supplied return it if input_parm then return false, input_parm end -- We can filter out non-valid properties if property_id:sub(1,1):upper() ~="P" or property_id == "P0" then return false, nil end -- Otherwise see if this field is on the whitelist: -- needs a bit more logic because find will return its second value = 0 if fieldname is "" -- but nil if fieldname not found on whitelist local _, found = whitelist:find(fieldname) found = ((found or 0) > 0) if whitelist ~= 'ALL' and (whitelist:upper() == "NONE" or not found) then return false, nil end -- See what's on Wikidata (the call always returns a table, but it may be empty): local props = {} if args.reqranks.b then props = mw.wikibase.getBestStatements(qid, property_id) else props = mw.wikibase.getAllStatements(qid, property_id) end if props[1] then return qid, props end -- no property on Wikidata return false, nil end ------------------------------------------------------------------------------- -- createicon assembles the "Edit at Wikidata" pen icon. -- It returns a wikitext string inside a span class="penicon" -- if entityID is nil or empty, the ID associated with current page is used -- langcode and propertyID may be nil or empty ------------------------------------------------------------------------------- -- Dependencies: i18n[]; ------------------------------------------------------------------------------- local createicon = function(langcode, entityID, propertyID) langcode = langcode or "" if not entityID or entityID == "" then entityID= mw.wikibase.getEntityIdForCurrentPage() end propertyID = propertyID or "" local icon = "&nbsp;<span class='penicon autoconfirmed-show'>[[" -- "&nbsp;<span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge .. i18n["filespace"] .. ":OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt=" .. i18n["editonwikidata"] .. "|link=https://www.wikidata.org/wiki/" .. entityID if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end if propertyID ~= "" then icon = icon .. "#" .. propertyID end icon = icon .. "|" .. i18n["editonwikidata"] .. "]]</span>" return icon end ------------------------------------------------------------------------------- -- assembleoutput takes the sequence table containing the property values -- and formats it according to switches given. It returns a string or nil. -- It uses the entityID (and optionally propertyID) to create a link in the pen icon. ------------------------------------------------------------------------------- -- Dependencies: parseParam(); ------------------------------------------------------------------------------- local assembleoutput = function(out, args, entityID, propertyID) -- sorted is a boolean passed to enable sorting of the values returned -- if nothing or an empty string is passed set it false -- if "false" or "no" or "0" is passed set it false local sorted = parseParam(args.sorted, false) -- noicon is a boolean passed to suppress the trailing "edit at Wikidata" icon -- for use when the value is processed further by the infobox -- if nothing or an empty string is passed set it false -- if "false" or "no" or "0" is passed set it false local noic = parseParam(args.noicon, false) -- list is the name of a template that a list of multiple values is passed through -- examples include "hlist" and "ubl" -- setting it to "prose" produces something like "1, 2, 3, and 4" local list = args.list or "" -- sep is a string that is used to separate multiple returned values -- if nothing or an empty string is passed set it to the default -- any double-quotes " are stripped out, so that spaces may be passed -- e.g. |sep=" - " local sepdefault = i18n["list separator"] local separator = args.sep or "" separator = string.gsub(separator, '"', '') if separator == "" then separator = sepdefault end -- collapse is a number that determines the maximum number of returned values -- before the output is collapsed. -- Zero or not a number result in no collapsing (default becomes 0). local collapse = tonumber(args.collapse) or 0 -- replacetext (rt) is a string that is returned instead of any non-empty Wikidata value -- this is useful for tracking and debugging local replacetext = mw.text.trim(args.rt or args.replacetext or "") -- if there's anything to return, then return a list -- comma-separated by default, but may be specified by the sep parameter -- optionally specify a hlist or ubl or a prose list, etc. local strout if #out > 0 then if sorted then table.sort(out) end -- if there's something to display and a pen icon is wanted, add it the end of the last value local hasdisplay = false for i, v in ipairs(out) do if v ~= i18n.missinginfocat then hasdisplay = true break end end if not noic and hasdisplay then out[#out] = out[#out] .. createicon(args.langobj.code, entityID, propertyID) end if list == "" then strout = table.concat(out, separator) elseif list:lower() == "prose" then strout = mw.text.listToText( out ) else strout = mw.getCurrentFrame():expandTemplate{title = list, args = out} end if collapse >0 and #out > collapse then strout = collapsediv .. strout .. "</div>" end else strout = nil -- no items had valid reference end if replacetext ~= "" and strout then strout = replacetext end return strout end ------------------------------------------------------------------------------- -- rendersnak takes a table (propval) containing the information stored on one property value -- and returns the value as a string and its language if monolingual text. -- It handles data of type: -- wikibase-item -- time -- string, url, commonsMedia, external-id -- quantity -- globe-coordinate -- monolingualtext -- It also requires linked, the link/pre/postfixes, uabbr, and the arguments passed from frame. -- The optional filter parameter allows quantities to be be filtered by unit Qid. ------------------------------------------------------------------------------- -- Dependencies: parseParam(); labelOrId(); i18n[]; dateFormat(); -- roundto(); decimalPrecision(); decimalToDMS(); linkedItem(); ------------------------------------------------------------------------------- local rendersnak = function(propval, args, linked, lpre, lpost, pre, post, uabbr, filter) lpre = lpre or "" lpost = lpost or "" pre = pre or "" post = post or "" args.lang = args.lang or findLang().code -- allow values to display a fixed text instead of label local dtxt = args.displaytext or args.dt if dtxt == "" then dtxt = nil end -- switch to use display of short name (P1813) instead of label local shortname = args.shortname or args.sn shortname = parseParam(shortname, false) local snak = propval.mainsnak or propval local dtype = snak.datatype local dv = snak.datavalue dv = dv and dv.value -- value and monolingual text language code returned local val, mlt if propval.rank and not args.reqranks[propval.rank:sub(1, 1)] then -- val is nil: value has a rank that isn't requested ------------------------------------ elseif snak.snaktype == "somevalue" then -- value is unknown val = i18n["Unknown"] ------------------------------------ elseif snak.snaktype == "novalue" then -- value is none -- val = "No value" -- don't return anything ------------------------------------ elseif dtype == "wikibase-item" then -- data type is a wikibase item: -- it's wiki-linked value, so output as link if enabled and possible local qnumber = dv.id if linked then val = linkedItem(qnumber, args) else -- no link wanted so check for display-text, otherwise test for lang code local label, islabel if dtxt then label = dtxt else label, islabel = labelOrId(qnumber) local langlabel = mw.wikibase.getLabelByLang(qnumber, args.lang) if langlabel then label = mw.text.nowiki( langlabel ) end end val = pre .. label .. post end -- test for link required ------------------------------------ elseif dtype == "time" then -- data type is time: -- time is in timestamp format -- date precision is integer per mediawiki -- output formatting according to preferences (y/dmy/mdy) -- BC format as BC or BCE -- plaindate is passed to disable looking for "sourcing cirumstances" -- or to set the adjectival form -- qualifiers (if any) is a nested table or nil -- lang is given, or user language, or site language -- -- Here we can check whether args.df has a value -- If not, use code from Module:Sandbox/RexxS/Getdateformat to set it from templates like {{Use mdy dates}} val = dateFormat(dv.time, dv.precision, args.df, args.bc, args.pd, propval.qualifiers, args.lang, "", dv.calendarmodel) ------------------------------------ -- data types which are strings: elseif dtype == "commonsMedia" or dtype == "external-id" or dtype == "string" or dtype == "url" then -- commonsMedia or external-id or string or url -- all have mainsnak.datavalue.value as string if (lpre == "" or lpre == ":") and lpost == "" then -- don't link if no linkpre/postfix or linkprefix is just ":" val = pre .. dv .. post elseif dtype == "external-id" then val = "[" .. lpre .. dv .. lpost .. " " .. pre .. dv .. post .. "]" else val = "[[" .. lpre .. dv .. lpost .. "|" .. pre .. dv .. post .. "]]" end -- check for link requested (i.e. either linkprefix or linkpostfix exists) ------------------------------------ -- data types which are quantities: elseif dtype == "quantity" then -- quantities have mainsnak.datavalue.value.amount and mainsnak.datavalue.value.unit -- the unit is of the form http://www.wikidata.org/entity/Q829073 -- -- implement a switch to turn on/off numerical formatting later local fnum = true -- -- a switch to turn on/off conversions - only for en-wiki local conv = parseParam(args.conv or args.convert, false) -- if we have conversions, we won't have formatted numbers or scales if conv then uabbr = true fnum = false args.scale = "0" end -- -- a switch to turn on/off showing units, default is true local showunits = parseParam(args.su or args.showunits, true) -- -- convert amount to a number local amount = tonumber(dv.amount) or i18n["NaN"] -- -- scale factor for millions, billions, etc. local sc = tostring(args.scale or ""):sub(1,1):lower() local scale if sc == "a" then -- automatic scaling if amount > 1e15 then scale = 12 elseif amount > 1e12 then scale = 9 elseif amount > 1e9 then scale = 6 elseif amount > 1e6 then scale = 3 else scale = 0 end else scale = tonumber(args.scale) or 0 if scale < 0 or scale > 12 then scale = 0 end scale = math.floor(scale/3) * 3 end local factor = 10^scale amount = amount / factor -- ranges: local range = "" -- check if upper and/or lower bounds are given and significant local upb = tonumber(dv.upperBound) local lowb = tonumber(dv.lowerBound) if upb and lowb then -- differences rounded to 2 sig fig: local posdif = roundto(upb - amount, 2) / factor local negdif = roundto(amount - lowb, 2) / factor upb, lowb = amount + posdif, amount - negdif -- round scaled numbers to integers or 4 sig fig if (scale > 0 or sc == "a") then if amount < 1e4 then amount = roundto(amount, 4) else amount = math.floor(amount + 0.5) end end if fnum then amount = args.langobj:formatNum( amount ) end if posdif ~= negdif then -- non-symmetrical range = " +" .. posdif .. " -" .. negdif elseif posdif ~= 0 then -- symmetrical and non-zero range = " ±" .. posdif else -- otherwise range is zero, so leave it as "" end else -- round scaled numbers to integers or 4 sig fig if (scale > 0 or sc == "a") then if amount < 1e4 then amount = roundto(amount, 4) else amount = math.floor(amount + 0.5) end end if fnum then amount = args.langobj:formatNum( amount ) end end -- unit names and symbols: -- extract the qid in the form 'Qnnn' from the value.unit url -- and then fetch the label from that - or symbol if unitabbr is true local unit = "" local usep = "" local usym = "" local unitqid = string.match( dv.unit, "(Q%d+)" ) if filter and unitqid ~= filter then return nil end if unitqid and showunits then local uname = mw.wikibase.getLabelByLang(unitqid, args.lang) or "" if uname ~= "" then usep, unit = " ", uname end if uabbr then -- see if there's a unit symbol (P5061) local unitsymbols = mw.wikibase.getBestStatements(unitqid, "P5061") -- construct fallback table, add local lang and multiple languages local fbtbl = mw.language.getFallbacksFor( args.lang ) table.insert( fbtbl, 1, args.lang ) table.insert( fbtbl, 1, "mul" ) local found = false for idx1, us in ipairs(unitsymbols) do for idx2, fblang in ipairs(fbtbl) do if us.mainsnak.datavalue.value.language == fblang then usym = us.mainsnak.datavalue.value.text found = true break end if found then break end end -- loop through fallback table end -- loop through values of P5061 if found then usep, unit = "&nbsp;", usym end end end -- format display: if conv then if range == "" then val = mw.getCurrentFrame():expandTemplate{title = "cvt", args = {amount, unit}} else val = mw.getCurrentFrame():expandTemplate{title = "cvt", args = {lowb, "to", upb, unit}} end elseif unit == "$" or unit == "£" then val = unit .. amount .. range .. i18n.multipliers[scale] else val = amount .. range .. i18n.multipliers[scale] .. usep .. unit end ------------------------------------ -- datatypes which are global coordinates: elseif dtype == "globe-coordinate" then -- 'display' parameter defaults to "inline, title" *** unused for now *** -- local disp = args.display or "" -- if disp == "" then disp = "inline, title" end -- -- format parameter switches from deg/min/sec to decimal degrees -- default is deg/min/sec -- decimal degrees needs |format = dec local form = (args.format or ""):lower():sub(1,3) if form ~= "dec" then form = "dms" end -- not needed for now -- -- show parameter allows just the latitude, or just the longitude, or both -- to be returned as a signed decimal, ignoring the format parameter. local show = (args.show or ""):lower() if show ~= "longlat" then show = show:sub(1,3) end -- local lat, long, prec = dv.latitude, dv.longitude, dv.precision if show == "lat" then val = decimalPrecision(lat, prec) elseif show == "lon" then val = decimalPrecision(long, prec) elseif show == "longlat" then val = decimalPrecision(long, prec) .. ", " .. decimalPrecision(lat, prec) else local ns = "N" local ew = "E" if lat < 0 then ns = "S" lat = - lat end if long < 0 then ew = "W" long = - long end if form == "dec" then lat = decimalPrecision(lat, prec) long = decimalPrecision(long, prec) val = lat .. "°" .. ns .. " " .. long .. "°" .. ew else local latdeg, latmin, latsec = decimalToDMS(lat, prec) local longdeg, longmin, longsec = decimalToDMS(long, prec) if latsec == 0 and longsec == 0 then if latmin == 0 and longmin == 0 then val = latdeg .. "°" .. ns .. " " .. longdeg .. "°" .. ew else val = latdeg .. "°" .. latmin .. "′" .. ns .. " " val = val .. longdeg .. "°".. longmin .. "′" .. ew end else val = latdeg .. "°" .. latmin .. "′" .. latsec .. "″" .. ns .. " " val = val .. longdeg .. "°" .. longmin .. "′" .. longsec .. "″" .. ew end end end ------------------------------------ elseif dtype == "monolingualtext" then -- data type is Monolingual text: -- has mainsnak.datavalue.value as a table containing language/text pairs -- collect all the values in 'out' and languages in 'mlt' and process them later val = pre .. dv.text .. post mlt = dv.language ------------------------------------ else -- some other data type so write a specific handler val = "unknown data type: " .. dtype end -- of datatype/unknown value/sourced check return val, mlt end ------------------------------------------------------------------------------- -- propertyvalueandquals takes a property object, the arguments passed from frame, -- and a qualifier propertyID. -- It returns a sequence (table) of values representing the values of that property -- and qualifiers that match the qualifierID if supplied. ------------------------------------------------------------------------------- -- Dependencies: parseParam(); sourced(); labelOrId(); i18n.latestdatequalifier(); format_Date(); -- makeOrdinal(); roundto(); decimalPrecision(); decimalToDMS(); assembleoutput(); ------------------------------------------------------------------------------- local function propertyvalueandquals(objproperty, args, qualID) -- needs this style of declaration because it's re-entrant -- onlysourced is a boolean passed to return only values sourced to other than Wikipedia -- if nothing or an empty string is passed set it true local onlysrc = parseParam(args.onlysourced or args.osd, true) -- linked is a a boolean that enables the link to a local page via sitelink -- if nothing or an empty string is passed set it true local linked = parseParam(args.linked, true) -- prefix is a string that may be nil, empty (""), or a string of characters -- this is prefixed to each value -- useful when when multiple values are returned -- any double-quotes " are stripped out, so that spaces may be passed local prefix = (args.prefix or ""):gsub('"', '') -- postfix is a string that may be nil, empty (""), or a string of characters -- this is postfixed to each value -- useful when when multiple values are returned -- any double-quotes " are stripped out, so that spaces may be passed local postfix = (args.postfix or ""):gsub('"', '') -- linkprefix is a string that may be nil, empty (""), or a string of characters -- this creates a link and is then prefixed to each value -- useful when when multiple values are returned and indirect links are needed -- any double-quotes " are stripped out, so that spaces may be passed local lprefix = (args.linkprefix or args.lp or ""):gsub('"', '') -- linkpostfix is a string that may be nil, empty (""), or a string of characters -- this is postfixed to each value when linking is enabled with lprefix -- useful when when multiple values are returned -- any double-quotes " are stripped out, so that spaces may be passed local lpostfix = (args.linkpostfix or ""):gsub('"', '') -- wdlinks is a boolean passed to enable links to Wikidata when no article exists -- if nothing or an empty string is passed set it false local wdl = parseParam(args.wdlinks or args.wdl, false) -- unitabbr is a boolean passed to enable unit abbreviations for common units -- if nothing or an empty string is passed set it false local uabbr = parseParam(args.unitabbr or args.uabbr, false) -- qualsonly is a boolean passed to return just the qualifiers -- if nothing or an empty string is passed set it false local qualsonly = parseParam(args.qualsonly or args.qo, false) -- maxvals is a string that may be nil, empty (""), or a number -- this determines how many items may be returned when multiple values are available -- setting it = 1 is useful where the returned string is used within another call, e.g. image local maxvals = tonumber(args.maxvals) or 0 -- pd (plain date) is a string: yes/true/1 | no/false/0 | adj -- to disable/enable "sourcing cirumstances" or use adjectival form for the plain date local pd = args.plaindate or args.pd or "no" args.pd = pd -- allow qualifiers to have a different date format; default to year unless qualsonly is set args.qdf = args.qdf or args.qualifierdateformat or args.df or (not qualsonly and "y") local lang = args.lang or findLang().code -- qualID is a string list of wanted qualifiers or "ALL" qualID = qualID or "" -- capitalise list of wanted qualifiers and substitute "DATES" qualID = qualID:upper():gsub("DATES", "P580, P582") local allflag = (qualID == "ALL") -- create table of wanted qualifiers as key local qwanted = {} -- create sequence of wanted qualifiers local qorder = {} for q in mw.text.gsplit(qualID, "%p") do -- split at punctuation and iterate local qtrim = mw.text.trim(q) if qtrim ~= "" then qwanted[mw.text.trim(q)] = true qorder[#qorder+1] = qtrim end end -- qsep is the output separator for rendering qualifier list local qsep = (args.qsep or ""):gsub('"', '') -- qargs are the arguments to supply to assembleoutput() local qargs = { ["osd"] = "false", ["linked"] = tostring(linked), ["prefix"] = args.qprefix, ["postfix"] = args.qpostfix, ["linkprefix"] = args.qlinkprefix or args.qlp, ["linkpostfix"] = args.qlinkpostfix, ["wdl"] = "false", ["unitabbr"] = tostring(uabbr), ["maxvals"] = 0, ["sorted"] = tostring(args.qsorted), ["noicon"] = "true", ["list"] = args.qlist, ["sep"] = qsep, ["langobj"] = args.langobj, ["lang"] = args.langobj.code, ["df"] = args.qdf, ["sn"] = parseParam(args.qsn or args.qshortname, false), } -- all proper values of a Wikidata property will be the same type as the first -- qualifiers don't have a mainsnak, properties do local datatype = objproperty[1].datatype or objproperty[1].mainsnak.datatype -- out[] holds the a list of returned values for this property -- mlt[] holds the language code if the datatype is monolingual text local out = {} local mlt = {} for k, v in ipairs(objproperty) do local hasvalue = true if (onlysrc and not sourced(v)) then -- no value: it isn't sourced when onlysourced=true hasvalue = false else local val, lcode = rendersnak(v, args, linked, lprefix, lpostfix, prefix, postfix, uabbr) if not val then hasvalue = false -- rank doesn't match elseif qualsonly and qualID then -- suppress value returned: only qualifiers are requested else out[#out+1], mlt[#out+1] = val, lcode end end -- See if qualifiers are to be returned: local snak = v.mainsnak or v if hasvalue and v.qualifiers and qualID ~= "" and snak.snaktype~="novalue" then -- collect all wanted qualifier values returned in qlist, indexed by propertyID local qlist = {} local timestart, timeend = "", "" -- loop through qualifiers for k1, v1 in pairs(v.qualifiers) do if allflag or qwanted[k1] then if k1 == "P1326" then local ts = v1[1].datavalue.value.time local dp = v1[1].datavalue.value.precision qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "before") elseif k1 == "P1319" then local ts = v1[1].datavalue.value.time local dp = v1[1].datavalue.value.precision qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "after") elseif k1 == "P580" then timestart = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one start time as valid elseif k1 == "P582" then timeend = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one end time as valid else local q = assembleoutput(propertyvalueandquals(v1, qargs), qargs) -- we already deal with circa via 'sourcing circumstances' if the datatype was time -- circa may be either linked or unlinked *** internationalise later *** if datatype ~= "time" or q ~= "circa" and not (type(q) == "string" and q:find("circa]]")) then qlist[k1] = q end end end -- of test for wanted end -- of loop through qualifiers -- set date separator local t = timestart .. timeend -- *** internationalise date separators later *** local dsep = "&ndash;" if t:find("%s") or t:find("&nbsp;") then dsep = " &ndash; " end -- set the order for the list of qualifiers returned; start time and end time go last if next(qlist) then local qlistout = {} if allflag then for k2, v2 in pairs(qlist) do qlistout[#qlistout+1] = v2 end else for i2, v2 in ipairs(qorder) do qlistout[#qlistout+1] = qlist[v2] end end if t ~= "" then qlistout[#qlistout+1] = timestart .. dsep .. timeend end local qstr = assembleoutput(qlistout, qargs) if qualsonly then out[#out+1] = qstr else out[#out] = out[#out] .. " (" .. qstr .. ")" end elseif t ~= "" then if qualsonly then if timestart == "" then out[#out+1] = timeend elseif timeend == "" then out[#out+1] = timestart else out[#out+1] = timestart .. dsep .. timeend end else out[#out] = out[#out] .. " (" .. timestart .. dsep .. timeend .. ")" end end end -- of test for qualifiers wanted if maxvals > 0 and #out >= maxvals then break end end -- of for each value loop -- we need to pick one value to return if the datatype was "monolingualtext" -- if there's only one value, use that -- otherwise look through the fallback languages for a match if datatype == "monolingualtext" and #out >1 then lang = mw.text.split( lang, '-', true )[1] local fbtbl = mw.language.getFallbacksFor( lang ) table.insert( fbtbl, 1, lang ) local bestval = "" local found = false for idx1, lang1 in ipairs(fbtbl) do for idx2, lang2 in ipairs(mlt) do if (lang1 == lang2) and not found then bestval = out[idx2] found = true break end end -- loop through values of property end -- loop through fallback languages if found then -- replace output table with a table containing the best value out = { bestval } else -- more than one value and none of them on the list of fallback languages -- sod it, just give them the first one out = { out[1] } end end return out end ------------------------------------------------------------------------------- -- Common code for p.getValueByQual and p.getValueByLang ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; assembleoutput; ------------------------------------------------------------------------------- local _getvaluebyqual = function(frame, qualID, checkvalue) -- The property ID that will have a qualifier is the first unnamed parameter local propertyID = mw.text.trim(frame.args[1] or "") if propertyID == "" then return "no property supplied" end if qualID == "" then return "no qualifier supplied" end -- onlysourced is a boolean passed to return property values -- only when property values are sourced to something other than Wikipedia -- if nothing or an empty string is passed set it true -- if "false" or "no" or 0 is passed set it false local onlysrc = parseParam(frame.args.onlysourced or frame.args.osd, true) -- set the requested ranks flags frame.args.reqranks = setRanks(frame.args.rank) -- set a language object and code in the frame.args table frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local args = frame.args -- check for locally supplied parameter in second unnamed parameter -- success means no local parameter and the property exists local qid, props = parseInput(frame, args[2], propertyID) local linked = parseParam(args.linked, true) local lpre = (args.linkprefix or args.lp or ""):gsub('"', '') local lpost = (args.linkpostfix or ""):gsub('"', '') local pre = (args.prefix or ""):gsub('"', '') local post = (args.postfix or ""):gsub('"', '') local uabbr = parseParam(args.unitabbr or args.uabbr, false) local filter = (args.unit or ""):upper() local maxvals = tonumber(args.maxvals) or 0 if filter == "" then filter = nil end if qid then local out = {} -- Scan through the values of the property -- we want something like property is "pronunciation audio (P443)" in propertyID -- with a qualifier like "language of work or name (P407)" in qualID -- whose value has the required ID, like "British English (Q7979)", in qval for k1, v1 in ipairs(props) do if v1.mainsnak.snaktype == "value" then -- check if it has the right qualifier local v1q = v1.qualifiers if v1q and v1q[qualID] then if onlysrc == false or sourced(v1) then -- if we've got this far, we have a (sourced) claim with qualifiers -- so see if matches the required value -- We'll only deal with wikibase-items and strings for now if v1q[qualID][1].datatype == "wikibase-item" then if checkvalue(v1q[qualID][1].datavalue.value.id) then out[#out + 1] = rendersnak(v1, args, linked, lpre, lpost, pre, post, uabbr, filter) end elseif v1q[qualID][1].datatype == "string" then if checkvalue(v1q[qualID][1].datavalue.value) then out[#out + 1] = rendersnak(v1, args, linked, lpre, lpost, pre, post, uabbr, filter) end end end -- of check for sourced end -- of check for matching required value and has qualifiers else return nil end -- of check for string if maxvals > 0 and #out >= maxvals then break end end -- of loop through values of propertyID return assembleoutput(out, frame.args, qid, propertyID) else return props -- either local parameter or nothing end -- of test for success return nil end ------------------------------------------------------------------------------- -- _location takes Q-id and follows P276 (location) -- or P131 (located in the administrative territorial entity) or P706 (located on terrain feature) -- from the initial item to higher level territories/locations until it reaches the highest. -- An optional boolean, 'first', determines whether the first item is returned (default: false). -- An optional boolean 'skip' toggles the display to skip to the last item (default: false). -- It returns a table containing the locations - linked where possible, except for the highest. ------------------------------------------------------------------------------- -- Dependencies: findLang(); labelOrId(); linkedItem ------------------------------------------------------------------------------- local _location = function(qid, first, skip) first = parseParam(first, false) skip = parseParam(skip, false) local locs = {"P276", "P131", "P706"} local out = {} local langcode = findLang():getCode() local finished = false local count = 0 local prevqid = "Q0" repeat local prop for i1, v1 in ipairs(locs) do local proptbl = mw.wikibase.getBestStatements(qid, v1) if #proptbl > 1 then -- there is more than one higher location local prevP131, prevP131id if prevqid ~= "Q0" then prevP131 = mw.wikibase.getBestStatements(prevqid, "P131")[1] prevP131id = prevP131 and prevP131.mainsnak.datavalue and prevP131.mainsnak.datavalue.value.id end for i2, v2 in ipairs(proptbl) do local parttbl = v2.qualifiers and v2.qualifiers.P518 if parttbl then -- this higher location has qualifier 'applies to part' (P518) for i3, v3 in ipairs(parttbl) do if v3.snaktype == "value" and v3.datavalue.value.id == prevqid then -- it has a value equal to the previous location prop = proptbl[i2] break end -- of test for matching last location end -- of loop through values of 'applies to part' else -- there's no qualifier 'applies to part' (P518) -- so check if the previous location had a P131 that matches this alternate if qid == prevP131id then prop = proptbl[i2] break end -- of test for matching previous P131 end end -- of loop through parent locations -- fallback to second value if match not found prop = prop or proptbl[2] elseif #proptbl > 0 then prop = proptbl[1] end if prop then break end end -- check if it's an instance of (P31) a country (Q6256) or sovereign state (Q3624078) -- and terminate the chain if it is local inst = mw.wikibase.getAllStatements(qid, "P31") if #inst > 0 then for k, v in ipairs(inst) do local instid = v.mainsnak.datavalue and v.mainsnak.datavalue.value.id -- stop if it's a country (or a country within the United Kingdom if skip is true) if instid == "Q6256" or instid == "Q3624078" or (skip and instid == "Q3336843") then prop = nil -- this will ensure this is treated as top-level location break end end end -- get the name of this location and update qid to point to the parent location if prop and prop.mainsnak.datavalue then if not skip or count == 0 then local args = { lprefix = ":" } out[#out+1] = linkedItem(qid, args) -- get a linked value if we can end qid, prevqid = prop.mainsnak.datavalue.value.id, qid else -- This is top-level location, so get short name except when this is the first item -- Use full label if there's no short name or this is the first item local prop1813 = mw.wikibase.getAllStatements(qid, "P1813") -- if there's a short name and this isn't the only item if prop1813[1] and (#out > 0)then local shortname -- short name is monolingual text, so look for match to the local language -- choose the shortest 'short name' in that language for k, v in pairs(prop1813) do if v.mainsnak.datavalue.value.language == langcode then local name = v.mainsnak.datavalue.value.text if (not shortname) or (#name < #shortname) then shortname = name end end end -- add the shortname if one is found, fallback to the label -- but skip it if it's "USA" if shortname ~= "USA" then out[#out+1] = shortname or labelOrId(qid) else if skip then out[#out+1] = "US" end end else -- no shortname, so just add the label local loc = labelOrId(qid) -- exceptions go here: if loc == "United States of America" then out[#out+1] = "United States" else out[#out+1] = loc end end finished = true end count = count + 1 until finished or count >= 10 -- limit to 10 levels to avoid infinite loops -- remove the first location if not required if not first then table.remove(out, 1) end -- we might have duplicate text for consecutive locations, so remove them if #out > 2 then local plain = {} for i, v in ipairs(out) do -- strip any links plain[i] = v:gsub("^%[%[[^|]*|", ""):gsub("]]$", "") end local idx = 2 repeat if plain[idx] == plain[idx-1] then -- duplicate found local removeidx = 0 if (plain[idx] ~= out[idx]) and (plain[idx-1] == out[idx-1]) then -- only second one is linked, so drop the first removeidx = idx - 1 elseif (plain[idx] == out[idx]) and (plain[idx-1] ~= out[idx-1]) then -- only first one is linked, so drop the second removeidx = idx else -- pick one removeidx = idx - (os.time()%2) end table.remove(out, removeidx) table.remove(plain, removeidx) else idx = idx +1 end until idx >= #out end return out end ------------------------------------------------------------------------------- -- _getsumofparts scans the property 'has part' (P527) for values matching a list. -- The list (args.vlist) consists of a string of Qids separated by spaces or any usual punctuation. -- If the matched values have a qualifer 'quantity' (P1114), those quantites are summed. -- The sum is returned as a number (i.e. 0 if none) -- a table of arguments is supplied implementing the usual parameters. ------------------------------------------------------------------------------- -- Dependencies: setRanks; parseParam; parseInput; sourced; assembleoutput; ------------------------------------------------------------------------------- local _getsumofparts = function(args) local vallist = (args.vlist or ""):upper() if vallist == "" then return end args.reqranks = setRanks(args.rank) local f = {} f.args = args local qid, props = parseInput(f, "", "P527") if not qid then return 0 end local onlysrc = parseParam(args.onlysourced or args.osd, true) local sum = 0 for k1, v1 in ipairs(props) do if (onlysrc == false or sourced(v1)) and v1.mainsnak.snaktype == "value" and v1.mainsnak.datavalue.type == "wikibase-entityid" and vallist:match( v1.mainsnak.datavalue.value.id ) and v1.qualifiers then local quals = v1.qualifiers["P1114"] if quals then for k2, v2 in ipairs(quals) do sum = sum + v2.datavalue.value.amount end end end end return sum end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- Public functions ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- _getValue makes the functionality of getValue available to other modules ------------------------------------------------------------------------------- -- Dependencies: setRanks; parseInput; propertyvalueandquals; assembleoutput; parseParam; sourced; -- labelOrId; i18n.latestdatequalifier; format_Date; makeOrdinal; roundto; decimalPrecision; decimalToDMS; ------------------------------------------------------------------------------- p._getValue = function(args) -- parameter sets for commonly used groups of parameters local paraset = tonumber(args.ps or args.parameterset or 0) if paraset == 1 then -- a common setting args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" elseif paraset == 2 then -- equivalent to raw args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" args.linked = "no" args.pd = "true" elseif paraset == 3 then -- third set goes here end -- implement eid parameter local eid = args.eid if eid == "" then return nil elseif eid then args.qid = eid end local propertyID = mw.text.trim(args[1] or "") args.reqranks = setRanks(args.rank) -- replacetext (rt) is a string that is returned instead of any non-empty Wikidata value -- this is useful for tracking and debugging, so we set fetchwikidata=ALL to fill the whitelist local replacetext = mw.text.trim(args.rt or args.replacetext or "") if replacetext ~= "" then args.fetchwikidata = "ALL" end local f = {} f.args = args local entityid, props = parseInput(f, f.args[2], propertyID) if not entityid then return props -- either the input parameter or nothing end -- qual is a string containing the property ID of the qualifier(s) to be returned -- if qual == "ALL" then all qualifiers returned -- if qual == "DATES" then qualifiers P580 (start time) and P582 (end time) returned -- if nothing or an empty string is passed set it nil -> no qualifiers returned local qualID = mw.text.trim(args.qual or ""):upper() if qualID == "" then qualID = nil end -- set a language object and code in the args table args.langobj = findLang(args.lang) args.lang = args.langobj.code -- table 'out' stores the return value(s): local out = propertyvalueandquals(props, args, qualID) -- format the table of values and return it as a string: return assembleoutput(out, args, entityid, propertyID) end ------------------------------------------------------------------------------- -- getValue is used to get the value(s) of a property -- The property ID is passed as the first unnamed parameter and is required. -- A locally supplied parameter may optionaly be supplied as the second unnamed parameter. -- The function will now also return qualifiers if parameter qual is supplied ------------------------------------------------------------------------------- -- Dependencies: _getValue; setRanks; parseInput; propertyvalueandquals; assembleoutput; parseParam; sourced; -- labelOrId; i18n.latestdatequalifier; format_Date; makeOrdinal; roundto; decimalPrecision; decimalToDMS; ------------------------------------------------------------------------------- p.getValue = function(frame) local args= frame.args if not args[1] then args = frame:getParent().args if not args[1] then return i18n.errors["No property supplied"] end end return p._getValue(args) end ------------------------------------------------------------------------------- -- getPreferredValue is used to get a value, -- (or a comma separated list of them if multiple values exist). -- If preferred ranks are set, it will return those values, otherwise values with normal ranks -- now redundant to getValue with |rank=best ------------------------------------------------------------------------------- -- Dependencies: p.getValue; setRanks; parseInput; propertyvalueandquals; assembleoutput; -- parseParam; sourced; labelOrId; i18n.latestdatequalifier; format_Date; -- makeOrdinal; roundto; decimalPrecision; decimalToDMS; ------------------------------------------------------------------------------- p.getPreferredValue = function(frame) frame.args.rank = "best" return p.getValue(frame) end ------------------------------------------------------------------------------- -- getCoords is used to get coordinates for display in an infobox -- whitelist and blacklist are implemented -- optional 'display' parameter is allowed, defaults to nil - was "inline, title" ------------------------------------------------------------------------------- -- Dependencies: setRanks(); parseInput(); decimalPrecision(); ------------------------------------------------------------------------------- p.getCoords = function(frame) local propertyID = "P625" -- if there is a 'display' parameter supplied, use it -- otherwise default to nothing local disp = frame.args.display or "" if disp == "" then disp = nil -- default to not supplying display parameter, was "inline, title" end -- there may be a format parameter to switch from deg/min/sec to decimal degrees -- default is deg/min/sec -- decimal degrees needs |format = dec local form = (frame.args.format or ""):lower():sub(1,3) if form ~= "dec" then form = "dms" end -- just deal with best values frame.args.reqranks = setRanks("best") local qid, props = parseInput(frame, frame.args[1], propertyID) if not qid then return props -- either local parameter or nothing else local dv = props[1].mainsnak.datavalue.value local lat, long, prec = dv.latitude, dv.longitude, dv.precision lat = decimalPrecision(lat, prec) long = decimalPrecision(long, prec) local lat_long = { lat, long } lat_long["display"] = disp lat_long["format"] = form -- invoke template Coord with the values stored in the table return frame:expandTemplate{title = 'coord', args = lat_long} end end ------------------------------------------------------------------------------- -- getQualifierValue is used to get a formatted value of a qualifier -- -- The call needs: a property (the unnamed parameter or 1=) -- a target value for that property (pval=) -- a qualifier for that target value (qual=) -- The usual whitelisting and blacklisting of the property is implemented -- The boolean onlysourced= parameter can be set to return nothing -- when the property is unsourced (or only sourced to Wikipedia) ------------------------------------------------------------------------------- -- Dependencies: parseParam(); setRanks(); parseInput(); sourced(); -- propertyvalueandquals(); assembleoutput(); -- labelOrId(); i18n.latestdatequalifier(); format_Date(); -- findLang(); makeOrdinal(); roundto(); decimalPrecision(); decimalToDMS(); ------------------------------------------------------------------------------- p.getQualifierValue = function(frame) -- The property ID that will have a qualifier is the first unnamed parameter local propertyID = mw.text.trim(frame.args[1] or "") -- The value of the property we want to match whose qualifier value is to be returned -- is passed in named parameter |pval= local propvalue = frame.args.pval -- The property ID of the qualifier -- whose value is to be returned is passed in named parameter |qual= local qualifierID = frame.args.qual -- A filter can be set like this: filter=P642==Q22674854 local filter, fprop, fval local ftable = mw.text.split(frame.args.filter or "", "==") if ftable[2] then fprop = mw.text.trim(ftable[1]) fval = mw.text.trim(ftable[2]) filter = true end -- onlysourced is a boolean passed to return qualifiers -- only when property values are sourced to something other than Wikipedia -- if nothing or an empty string is passed set it true -- if "false" or "no" or 0 is passed set it false local onlysrc = parseParam(frame.args.onlysourced or frame.args.osd, true) -- set a language object and language code in the frame.args table frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code -- set the requested ranks flags frame.args.reqranks = setRanks(frame.args.rank) -- check for locally supplied parameter in second unnamed parameter -- success means no local parameter and the property exists local qid, props = parseInput(frame, frame.args[2], propertyID) if qid then local out = {} -- Scan through the values of the property -- we want something like property is P793, significant event (in propertyID) -- whose value is something like Q385378, construction (in propvalue) -- then we can return the value(s) of a qualifier such as P580, start time (in qualifierID) for k1, v1 in pairs(props) do if v1.mainsnak.snaktype == "value" and v1.mainsnak.datavalue.type == "wikibase-entityid" then -- It's a wiki-linked value, so check if it's the target (in propvalue) and if it has qualifiers if v1.mainsnak.datavalue.value.id == propvalue and v1.qualifiers then if onlysrc == false or sourced(v1) then -- if we've got this far, we have a (sourced) claim with qualifiers -- which matches the target, so apply the filter and find the value(s) of the qualifier we want if not filter or (v1.qualifiers[fprop] and v1.qualifiers[fprop][1].datavalue.value.id == fval) then local quals = v1.qualifiers[qualifierID] if quals then -- can't reference qualifer, so set onlysourced = "no" (args are strings, not boolean) local qargs = frame.args qargs.onlysourced = "no" local vals = propertyvalueandquals(quals, qargs, qid) for k, v in ipairs(vals) do out[#out + 1] = v end end end end -- of check for sourced end -- of check for matching required value and has qualifiers end -- of check for wikibase entity end -- of loop through values of propertyID return assembleoutput(out, frame.args, qid, propertyID) else return props -- either local parameter or nothing end -- of test for success return nil end ------------------------------------------------------------------------------- -- getSumOfParts scans the property 'has part' (P527) for values matching a list. -- The list is passed in parameter vlist. -- It consists of a string of Qids separated by spaces or any usual punctuation. -- If the matched values have a qualifier 'quantity' (P1114), those quantities are summed. -- The sum is returned as a number or nothing if zero. ------------------------------------------------------------------------------- -- Dependencies: _getsumofparts; ------------------------------------------------------------------------------- p.getSumOfParts = function(frame) local sum = _getsumofparts(frame.args) if sum == 0 then return end return sum end ------------------------------------------------------------------------------- -- getValueByQual gets the value of a property which has a qualifier with a given entity value -- The call needs: -- a property ID (the unnamed parameter or 1=Pxxx) -- the ID of a qualifier for that property (qualID=Pyyy) -- either the Wikibase-entity ID of a value for that qualifier (qvalue=Qzzz) -- or a string value for that qualifier (qvalue=abc123) -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: _getvaluebyqual; parseParam; setRanks; parseInput; sourced; -- assembleoutput; ------------------------------------------------------------------------------- p.getValueByQual = function(frame) local qualID = frame.args.qualID -- The Q-id of the value for the qualifier we want to match is in named parameter |qvalue= local qval = frame.args.qvalue or "" if qval == "" then return "no qualifier value supplied" end local function checkQID(id) return id == qval end return _getvaluebyqual(frame, qualID, checkQID) end ------------------------------------------------------------------------------- -- getValueByLang gets the value of a property which has a qualifier P407 -- ("language of work or name") whose value has the given language code -- The call needs: -- a property ID (the unnamed parameter or 1=Pxxx) -- the MediaWiki language code to match the language (lang=xx[-yy]) -- (if no code is supplied, it uses the default language) -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: _getvaluebyqual; parseParam; setRanks; parseInput; sourced; assembleoutput; ------------------------------------------------------------------------------- p.getValueByLang = function(frame) -- The language code for the qualifier we want to match is in named parameter |lang= local langcode = findLang(frame.args.lang).code local function checkLanguage(id) -- id should represent a language like "British English (Q7979)" -- it should have string property "Wikimedia language code (P424)" -- qlcode will be a table: local qlcode = mw.wikibase.getBestStatements(id, "P424") if (#qlcode > 0) and (qlcode[1].mainsnak.datavalue.value == langcode) then return true end end return _getvaluebyqual(frame, "P407", checkLanguage) end ------------------------------------------------------------------------------- -- getValueByRefSource gets the value of a property which has a reference "stated in" (P248) -- whose value has the given entity-ID. -- The call needs: -- a property ID (the unnamed parameter or 1=Pxxx) -- the entity ID of a value to match where the reference is stated in (match=Qzzz) -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getValueByRefSource = function(frame) -- The property ID that we want to check is the first unnamed parameter local propertyID = mw.text.trim(frame.args[1] or ""):upper() if propertyID == "" then return "no property supplied" end -- The Q-id of the value we want to match is in named parameter |qvalue= local qval = (frame.args.match or ""):upper() if qval == "" then qval = "Q21540096" end local unit = (frame.args.unit or ""):upper() if unit == "" then unit = "Q4917" end local onlysrc = parseParam(frame.args.onlysourced or frame.args.osd, true) -- set the requested ranks flags frame.args.reqranks = setRanks(frame.args.rank) -- set a language object and code in the frame.args table frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local linked = parseParam(frame.args.linked, true) local uabbr = parseParam(frame.args.uabbr or frame.args.unitabbr, false) -- qid not nil means no local parameter and the property exists local qid, props = parseInput(frame, frame.args[2], propertyID) if qid then local out = {} local mlt= {} for k1, v1 in ipairs(props) do if onlysrc == false or sourced(v1) then if v1.references then for k2, v2 in ipairs(v1.references) do if v2.snaks.P248 then for k3, v3 in ipairs(v2.snaks.P248) do if v3.datavalue.value.id == qval then out[#out+1], mlt[#out+1] = rendersnak(v1, frame.args, linked, "", "", "", "", uabbr, unit) if not mlt[#out] then -- we only need one match per property value -- unless datatype was monolingual text break end end -- of test for match end -- of loop through values "stated in" end -- of test that "stated in" exists end -- of loop through references end -- of test that references exist end -- of test for sourced end -- of loop through values of propertyID if #mlt > 0 then local langcode = frame.args.lang langcode = mw.text.split( langcode, '-', true )[1] local fbtbl = mw.language.getFallbacksFor( langcode ) table.insert( fbtbl, 1, langcode ) local bestval = "" local found = false for idx1, lang1 in ipairs(fbtbl) do for idx2, lang2 in ipairs(mlt) do if (lang1 == lang2) and not found then bestval = out[idx2] found = true break end end -- loop through values of property end -- loop through fallback languages if found then -- replace output table with a table containing the best value out = { bestval } else -- more than one value and none of them on the list of fallback languages -- sod it, just give them the first one out = { out[1] } end end return assembleoutput(out, frame.args, qid, propertyID) else return props -- no property or local parameter supplied end -- of test for success end ------------------------------------------------------------------------------- -- getPropertyIDs takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented. -- It returns the Entity-IDs (Qids) of the values of a property if it is a Wikibase-Entity. -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p._getPropertyIDs = function(args) args.reqranks = setRanks(args.rank) args.langobj = findLang(args.lang) args.lang = args.langobj.code -- change default for noicon to true args.noicon = tostring(parseParam(args.noicon or "", true)) local f = {} f.args = args local pid = mw.text.trim(args[1] or ""):upper() -- get the qid and table of claims for the property, or nothing and the local value passed local qid, props = parseInput(f, args[2], pid) if not qid then return props end if not props[1] then return nil end local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local out = {} for i, v in ipairs(props) do local snak = v.mainsnak if ( snak.datatype == "wikibase-item" ) and ( v.rank and args.reqranks[v.rank:sub(1, 1)] ) and ( snak.snaktype == "value" ) and ( sourced(v) or not onlysrc ) then out[#out+1] = snak.datavalue.value.id end if maxvals > 0 and #out >= maxvals then break end end return assembleoutput(out, args, qid, pid) end p.getPropertyIDs = function(frame) local args = frame.args return p._getPropertyIDs(args) end ------------------------------------------------------------------------------- -- getQualifierIDs takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented. -- It takes a property-id as the first unnamed parameter, and an optional parameter qlist -- which is a list of qualifier property-ids to search for (default is "ALL") -- It returns the Entity-IDs (Qids) of the values of a property if it is a Wikibase-Entity. -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getQualifierIDs = function(frame) local args = frame.args args.reqranks = setRanks(args.rank) args.langobj = findLang(args.lang) args.lang = args.langobj.code -- change default for noicon to true args.noicon = tostring(parseParam(args.noicon or "", true)) local f = {} f.args = args local pid = mw.text.trim(args[1] or ""):upper() -- get the qid and table of claims for the property, or nothing and the local value passed local qid, props = parseInput(f, args[2], pid) if not qid then return props end if not props[1] then return nil end -- get the other parameters local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local qlist = args.qlist or "" if qlist == "" then qlist = "ALL" end qlist = qlist:gsub("[%p%s]+", " ") .. " " local out = {} for i, v in ipairs(props) do local snak = v.mainsnak if ( v.rank and args.reqranks[v.rank:sub(1, 1)] ) and ( snak.snaktype == "value" ) and ( sourced(v) or not onlysrc ) then if v.qualifiers then for k1, v1 in pairs(v.qualifiers) do if qlist == "ALL " or qlist:match(k1 .. " ") then for i2, v2 in ipairs(v1) do if v2.datatype == "wikibase-item" and v2.snaktype == "value" then out[#out+1] = v2.datavalue.value.id end -- of test that id exists end -- of loop through qualifier values end -- of test for kq in qlist end -- of loop through qualifiers end -- of test for qualifiers end -- of test for rank value, sourced, and value exists if maxvals > 0 and #out >= maxvals then break end end -- of loop through property values return assembleoutput(out, args, qid, pid) end ------------------------------------------------------------------------------- -- getPropOfProp takes two propertyIDs: prop1 and prop2 (as well as the usual parameters) -- If the value(s) of prop1 are of type "wikibase-item" then it returns the value(s) of prop2 -- of each of those wikibase-items. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p._getPropOfProp = function(args) -- parameter sets for commonly used groups of parameters local paraset = tonumber(args.ps or args.parameterset or 0) if paraset == 1 then -- a common setting args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" elseif paraset == 2 then -- equivalent to raw args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" args.linked = "no" args.pd = "true" elseif paraset == 3 then -- third set goes here end args.reqranks = setRanks(args.rank) args.langobj = findLang(args.lang) args.lang = args.langobj.code local pid1 = args.prop1 or args.pid1 or "" local pid2 = args.prop2 or args.pid2 or "" if pid1 == "" or pid2 == "" then return nil end local f = {} f.args = args local qid1, statements1 = parseInput(f, args[1], pid1) -- parseInput nulls empty args[1] and returns args[1] if nothing on Wikidata if not qid1 then return statements1 end -- otherwise it returns the qid and a table for the statement local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local qualID = mw.text.trim(args.qual or ""):upper() if qualID == "" then qualID = nil end local out = {} for k, v in ipairs(statements1) do if not onlysrc or sourced(v) then local snak = v.mainsnak if snak.datatype == "wikibase-item" and snak.snaktype == "value" then local qid2 = snak.datavalue.value.id local statements2 = {} if args.reqranks.b then statements2 = mw.wikibase.getBestStatements(qid2, pid2) else statements2 = mw.wikibase.getAllStatements(qid2, pid2) end if statements2[1] then local out2 = propertyvalueandquals(statements2, args, qualID) out[#out+1] = assembleoutput(out2, args, qid2, pid2) end end -- of test for valid property1 value end -- of test for sourced if maxvals > 0 and #out >= maxvals then break end end -- of loop through values of property1 return assembleoutput(out, args, qid1, pid1) end p.getPropOfProp = function(frame) local args= frame.args if not args.prop1 and not args.pid1 then args = frame:getParent().args if not args.prop1 and not args.pid1 then return i18n.errors["No property supplied"] end end return p._getPropOfProp(args) end ------------------------------------------------------------------------------- -- getAwardCat takes most of the usual parameters. If the item has values of P166 (award received), -- then it examines each of those awards for P2517 (category for recipients of this award). -- If it exists, it returns the corresponding category, -- with the item's P734 (family name) as sort key, or no sort key if there is no family name. -- The sort key may be overridden by the parameter |sortkey (alias |sk). -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getAwardCat = function(frame) frame.args.reqranks = setRanks(frame.args.rank) frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local args = frame.args args.sep = " " local pid1 = args.prop1 or "P166" local pid2 = args.prop2 or "P2517" if pid1 == "" or pid2 == "" then return nil end -- locally supplied value: local localval = mw.text.trim(args[1] or "") local qid1, statements1 = parseInput(frame, localval, pid1) if not qid1 then return localval end -- linkprefix (strip quotes) local lp = (args.linkprefix or args.lp or ""):gsub('"', '') -- sort key (strip quotes, hyphens and periods): local sk = (args.sortkey or args.sk or ""):gsub('["-.]', '') -- family name: local famname = "" if sk == "" then local p734 = mw.wikibase.getBestStatements(qid1, "P734")[1] local p734id = p734 and p734.mainsnak.snaktype == "value" and p734.mainsnak.datavalue.value.id or "" famname = mw.wikibase.getSitelink(p734id) or "" -- strip namespace and disambigation local pos = famname:find(":") or 0 famname = famname:sub(pos+1):gsub("%s%(.+%)$", "") if famname == "" then local lbl = mw.wikibase.getLabel(p734id) famname = lbl and mw.text.nowiki(lbl) or "" end end local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local qualID = mw.text.trim(args.qual or ""):upper() if qualID == "" then qualID = nil end local out = {} for k, v in ipairs(statements1) do if not onlysrc or sourced(v) then local snak = v.mainsnak if snak.datatype == "wikibase-item" and snak.snaktype == "value" then local qid2 = snak.datavalue.value.id local statements2 = {} if args.reqranks.b then statements2 = mw.wikibase.getBestStatements(qid2, pid2) else statements2 = mw.wikibase.getAllStatements(qid2, pid2) end if statements2[1] and statements2[1].mainsnak.snaktype == "value" then local qid3 = statements2[1].mainsnak.datavalue.value.id local sitelink = mw.wikibase.getSitelink(qid3) -- if there's no local sitelink, create the sitelink from English label if not sitelink then local lbl = mw.wikibase.getLabelByLang(qid3, "en") if lbl then if lbl:sub(1,9) == "Category:" then sitelink = mw.text.nowiki(lbl) else sitelink = "Category:" .. mw.text.nowiki(lbl) end end end if sitelink then if sk ~= "" then out[#out+1] = "[[" .. lp .. sitelink .. "|" .. sk .. "]]" elseif famname ~= "" then out[#out+1] = "[[" .. lp .. sitelink .. "|" .. famname .. "]]" else out[#out+1] = "[[" .. lp .. sitelink .. "]]" end -- of check for sort keys end -- of test for sitelink end -- of test for category end -- of test for wikibase item has a value end -- of test for sourced if maxvals > 0 and #out >= maxvals then break end end -- of loop through values of property1 return assembleoutput(out, args, qid1, pid1) end ------------------------------------------------------------------------------- -- getIntersectCat takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented -- It takes two properties, |prop1 and |prop2 (e.g. occupation and country of citizenship) -- Each property's value is a wiki-base entity -- For each value of the first parameter (ranks implemented) it fetches the value's main category -- and then each value of the second parameter (possibly substituting a simpler description) -- then it returns all of the categories representing the intersection of those properties, -- (e.g. Category:Actors from Canada). A joining term may be supplied (e.g. |join=from). -- The item's P734 (family name) is the sort key, or no sort key if there is no family name. -- The sort key may be overridden by the parameter |sortkey (alias |sk). ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getIntersectCat = function(frame) frame.args.reqranks = setRanks(frame.args.rank) frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local args = frame.args args.sep = " " args.linked = "no" local pid1 = args.prop1 or "P106" local pid2 = args.prop2 or "P27" if pid1 == "" or pid2 == "" then return nil end local qid, statements1 = parseInput(frame, "", pid1) if not qid then return nil end local qid, statements2 = parseInput(frame, "", pid2) if not qid then return nil end -- topics like countries may have different names in categories from their label in Wikidata local subs_exists, subs = pcall(mw.loadData, "Module:WikidataIB/subs") local join = args.join or "" local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 -- linkprefix (strip quotes) local lp = (args.linkprefix or args.lp or ""):gsub('"', '') -- sort key (strip quotes, hyphens and periods): local sk = (args.sortkey or args.sk or ""):gsub('["-.]', '') -- family name: local famname = "" if sk == "" then local p734 = mw.wikibase.getBestStatements(qid, "P734")[1] local p734id = p734 and p734.mainsnak.snaktype == "value" and p734.mainsnak.datavalue.value.id or "" famname = mw.wikibase.getSitelink(p734id) or "" -- strip namespace and disambigation local pos = famname:find(":") or 0 famname = famname:sub(pos+1):gsub("%s%(.+%)$", "") if famname == "" then local lbl = mw.wikibase.getLabel(p734id) famname = lbl and mw.text.nowiki(lbl) or "" end end local cat1 = {} for k, v in ipairs(statements1) do if not onlysrc or sourced(v) then -- get the ID representing the value of the property local pvalID = (v.mainsnak.snaktype == "value") and v.mainsnak.datavalue.value.id if pvalID then -- get the topic's main category (P910) for that entity local p910 = mw.wikibase.getBestStatements(pvalID, "P910")[1] if p910 and p910.mainsnak.snaktype == "value" then local tmcID = p910.mainsnak.datavalue.value.id -- use sitelink or the English label for the cat local cat = mw.wikibase.getSitelink(tmcID) if not cat then local lbl = mw.wikibase.getLabelByLang(tmcID, "en") if lbl then if lbl:sub(1,9) == "Category:" then cat = mw.text.nowiki(lbl) else cat = "Category:" .. mw.text.nowiki(lbl) end end end cat1[#cat1+1] = cat end -- of test for topic's main category exists end -- of test for property has vaild value end -- of test for sourced if maxvals > 0 and #cat1 >= maxvals then break end end local cat2 = {} for k, v in ipairs(statements2) do if not onlysrc or sourced(v) then local cat = rendersnak(v, args) if subs[cat] then cat = subs[cat] end cat2[#cat2+1] = cat end if maxvals > 0 and #cat2 >= maxvals then break end end local out = {} for k1, v1 in ipairs(cat1) do for k2, v2 in ipairs(cat2) do if sk ~= "" then out[#out+1] = "[[" .. lp .. v1 .. " " .. join .. " " .. v2 .. "|" .. sk .. "]]" elseif famname ~= "" then out[#out+1] = "[[" .. lp .. v1 .. " " .. join .. " " .. v2 .. "|" .. famname .. "]]" else out[#out+1] = "[[" .. lp .. v1 .. " " .. join .. " " .. v2 .. "]]" end -- of check for sort keys end end args.noicon = "true" return assembleoutput(out, args, qid, pid1) end ------------------------------------------------------------------------------- -- qualsToTable takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented. -- A qid may be given, and the first unnamed parameter is the property ID, which is of type wikibase item. -- It takes a list of qualifier property IDs as |quals= -- For a given qid and property, it creates the rows of an html table, -- each row being a value of the property (optionally only if the property matches the value in |pval= ) -- each cell being the first value of the qualifier corresponding to the list in |quals ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; ------------------------------------------------------------------------------- p.qualsToTable = function(frame) local args = frame.args local quals = args.quals or "" if quals == "" then return "" end args.reqranks = setRanks(args.rank) local propertyID = mw.text.trim(args[1] or "") local f = {} f.args = args local entityid, props = parseInput(f, "", propertyID) if not entityid then return "" end args.langobj = findLang(args.lang) args.lang = args.langobj.code local pval = args.pval or "" local qplist = mw.text.split(quals, "%p") -- split at punctuation and make a sequential table for i, v in ipairs(qplist) do qplist[i] = mw.text.trim(v):upper() -- remove whitespace and capitalise end local col1 = args.firstcol or "" if col1 ~= "" then col1 = col1 .. "</td><td>" end local emptycell = args.emptycell or "&nbsp;" -- construct a 2-D array of qualifier values in qvals local qvals = {} for i, v in ipairs(props) do local skip = false if pval ~= "" then local pid = v.mainsnak.datavalue and v.mainsnak.datavalue.value.id if pid ~= pval then skip = true end end if not skip then local qval = {} local vqualifiers = v.qualifiers or {} -- go through list of wanted qualifier properties for i1, v1 in ipairs(qplist) do -- check for that property ID in the statement's qualifiers local qv, qtype if vqualifiers[v1] then qtype = vqualifiers[v1][1].datatype if qtype == "time" then if vqualifiers[v1][1].snaktype == "value" then qv = mw.wikibase.renderSnak(vqualifiers[v1][1]) qv = frame:expandTemplate{title="dts", args={qv}} else qv = "?" end elseif qtype == "url" then if vqualifiers[v1][1].snaktype == "value" then qv = mw.wikibase.renderSnak(vqualifiers[v1][1]) local display = mw.ustring.match( mw.uri.decode(qv, "WIKI"), "([%w ]+)$" ) if display then qv = "[" .. qv .. " " .. display .. "]" end end else qv = mw.wikibase.formatValue(vqualifiers[v1][1]) end end -- record either the value or a placeholder qval[i1] = qv or emptycell end -- of loop through list of qualifiers -- add the list of qualifier values as a "row" in the main list qvals[#qvals+1] = qval end end -- of for each value loop local out = {} for i, v in ipairs(qvals) do out[i] = "<tr><td>" .. col1 .. table.concat(qvals[i], "</td><td>") .. "</td></tr>" end return table.concat(out, "\n") end ------------------------------------------------------------------------------- -- getGlobe takes an optional qid of a Wikidata entity passed as |qid= -- otherwise it uses the linked item for the current page. -- If returns the Qid of the globe used in P625 (coordinate location), -- or nil if there isn't one. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getGlobe = function(frame) local qid = frame.args.qid or frame.args[1] or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end local coords = mw.wikibase.getBestStatements(qid, "P625")[1] local globeid if coords and coords.mainsnak.snaktype == "value" then globeid = coords.mainsnak.datavalue.value.globe:match("(Q%d+)") end return globeid end ------------------------------------------------------------------------------- -- getCommonsLink takes an optional qid of a Wikidata entity passed as |qid= -- It returns one of the following in order of preference: -- the Commons sitelink of the linked Wikidata item; -- the Commons sitelink of the topic's main category of the linked Wikidata item; ------------------------------------------------------------------------------- -- Dependencies: _getCommonslink(); _getSitelink(); parseParam() ------------------------------------------------------------------------------- p.getCommonsLink = function(frame) local oc = frame.args.onlycat or frame.args.onlycategories local fb = parseParam(frame.args.fallback or frame.args.fb, true) return _getCommonslink(frame.args.qid, oc, fb) end ------------------------------------------------------------------------------- -- getSitelink takes the qid of a Wikidata entity passed as |qid= -- It takes an optional parameter |wiki= to determine which wiki is to be checked for a sitelink -- If the parameter is blank, then it uses the local wiki. -- If there is a sitelink to an article available, it returns the plain text link to the article -- If there is no sitelink, it returns nil. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getSiteLink = function(frame) return _getSitelink(frame.args.qid, frame.args.wiki or mw.text.trim(frame.args[1] or "")) end ------------------------------------------------------------------------------- -- getLink has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- If there is a sitelink to an article on the local Wiki, it returns a link to the article -- with the Wikidata label as the displayed text. -- If there is no sitelink, it returns the label as plain text. -- If there is no label in the local language, it displays the qid instead. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getLink = function(frame) local itemID = mw.text.trim(frame.args[1] or frame.args.qid or "") if itemID == "" then return end local sitelink = mw.wikibase.getSitelink(itemID) local label = labelOrId(itemID) if sitelink then return "[[:" .. sitelink .. "|" .. label .. "]]" else return label end end ------------------------------------------------------------------------------- -- getLabel has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- It returns the Wikidata label for the local language as plain text. -- If there is no label in the local language, it displays the qid instead. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getLabel = function(frame) local itemID = mw.text.trim(frame.args[1] or frame.args.qid or "") if itemID == "" then return end local lang = frame.args.lang or "" if lang == "" then lang = nil end local label = labelOrId(itemID, lang) return label end ------------------------------------------------------------------------------- -- label has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- if no qid is supplied, it uses the qid associated with the current page. -- It returns the Wikidata label for the local language as plain text. -- If there is no label in the local language, it returns nil. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.label = function(frame) local qid = mw.text.trim(frame.args[1] or frame.args.qid or "") if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return end local lang = frame.args.lang or "" if lang == "" then lang = nil end local label, success = labelOrId(qid, lang) if success then return label end end ------------------------------------------------------------------------------- -- getAT (Article Title) -- has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- If there is a sitelink to an article on the local Wiki, it returns the sitelink as plain text. -- If there is no sitelink or qid supplied, it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAT = function(frame) local itemID = mw.text.trim(frame.args[1] or frame.args.qid or "") if itemID == "" then return end return mw.wikibase.getSitelink(itemID) end ------------------------------------------------------------------------------- -- getDescription has the qid of a Wikidata entity passed as |qid= -- (it defaults to the associated qid of the current article if omitted) -- and a local parameter passed as the first unnamed parameter. -- Any local parameter passed (other than "Wikidata" or "none") becomes the return value. -- It returns the article description for the Wikidata entity if the local parameter is "Wikidata". -- Nothing is returned if the description doesn't exist or "none" is passed as the local parameter. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getDescription = function(frame) local desc = mw.text.trim(frame.args[1] or "") local itemID = mw.text.trim(frame.args.qid or "") if itemID == "" then itemID = nil end if desc:lower() == 'wikidata' then return mw.wikibase.getDescription(itemID) elseif desc:lower() == 'none' then return nil else return desc end end ------------------------------------------------------------------------------- -- getAliases has the qid of a Wikidata entity passed as |qid= -- (it defaults to the associated qid of the current article if omitted) -- and a local parameter passed as the first unnamed parameter. -- It implements blacklisting and whitelisting with a field name of "alias" by default. -- Any local parameter passed becomes the return value. -- Otherwise it returns the aliases for the Wikidata entity with the usual list options. -- Nothing is returned if the aliases do not exist. ------------------------------------------------------------------------------- -- Dependencies: findLang(); assembleoutput() ------------------------------------------------------------------------------- p.getAliases = function(frame) local args = frame.args local fieldname = args.name or "" if fieldname == "" then fieldname = "alias" end local blacklist = args.suppressfields or args.spf or "" if blacklist:find(fieldname) then return nil end local localval = mw.text.trim(args[1] or "") if localval ~= "" then return localval end local whitelist = args.fetchwikidata or args.fwd or "" if whitelist == "" then whitelist = "NONE" end if not (whitelist == 'ALL' or whitelist:find(fieldname)) then return nil end local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return nil end local aliases = mw.wikibase.getEntity(qid).aliases if not aliases then return nil end args.langobj = findLang(args.lang) local langcode = args.langobj.code args.lang = langcode local out = {} for k1, v1 in pairs(aliases) do if v1[1].language == langcode then for k1, v2 in ipairs(v1) do out[#out+1] = v2.value end break end end return assembleoutput(out, args, qid) end ------------------------------------------------------------------------------- -- pageId returns the page id (entity ID, Qnnn) of the current page -- returns nothing if the page is not connected to Wikidata ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.pageId = function(frame) return mw.wikibase.getEntityIdForCurrentPage() end ------------------------------------------------------------------------------- -- formatDate is a wrapper to export the private function format_Date ------------------------------------------------------------------------------- -- Dependencies: format_Date(); ------------------------------------------------------------------------------- p.formatDate = function(frame) return format_Date(frame.args[1], frame.args.df, frame.args.bc) end ------------------------------------------------------------------------------- -- location is a wrapper to export the private function _location -- it takes the entity-id as qid or the first unnamed parameter -- optional boolean parameter first toggles the display of the first item -- optional boolean parameter skip toggles the display to skip to the last item -- parameter debug=<y/n> (default 'n') adds error msg if not a location ------------------------------------------------------------------------------- -- Dependencies: _location(); ------------------------------------------------------------------------------- p.location = function(frame) local debug = (frame.args.debug or ""):sub(1, 1):lower() if debug == "" then debug = "n" end local qid = mw.text.trim(frame.args.qid or frame.args[1] or ""):upper() if qid == "" then qid=mw.wikibase.getEntityIdForCurrentPage() end if not qid then if debug ~= "n" then return i18n.errors["entity-not-found"] else return nil end end local first = mw.text.trim(frame.args.first or "") local skip = mw.text.trim(frame.args.skip or "") return table.concat( _location(qid, first, skip), ", " ) end ------------------------------------------------------------------------------- -- checkBlacklist implements a test to check whether a named field is allowed -- returns true if the field is not blacklisted (i.e. allowed) -- returns false if the field is blacklisted (i.e. disallowed) -- {{#if:{{#invoke:WikidataIB |checkBlacklist |name=Joe |suppressfields=Dave; Joe; Fred}} | not blacklisted | blacklisted}} -- displays "blacklisted" -- {{#if:{{#invoke:WikidataIB |checkBlacklist |name=Jim |suppressfields=Dave; Joe; Fred}} | not blacklisted | blacklisted}} -- displays "not blacklisted" ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.checkBlacklist = function(frame) local blacklist = frame.args.suppressfields or frame.args.spf or "" local fieldname = frame.args.name or "" if blacklist ~= "" and fieldname ~= "" then if blacklist:find(fieldname) then return false else return true end else -- one of the fields is missing: let's call that "not on the list" return true end end ------------------------------------------------------------------------------- -- emptyor returns nil if its first unnamed argument is just punctuation, whitespace or html tags -- otherwise it returns the argument unchanged (including leading/trailing space). -- If the argument may contain "=", then it must be called explicitly: -- |1=arg -- (In that case, leading and trailing spaces are trimmed) -- It finds use in infoboxes where it can replace tests like: -- {{#if: {{#invoke:WikidatIB |getvalue |P99 |fwd=ALL}} | <span class="xxx">{{#invoke:WikidatIB |getvalue |P99 |fwd=ALL}}</span> | }} -- with a form that uses just a single call to Wikidata: -- {{#invoke |WikidataIB |emptyor |1= <span class="xxx">{{#invoke:WikidataIB |getvalue |P99 |fwd=ALL}}</span> }} ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.emptyor = function(frame) local s = frame.args[1] or "" if s == "" then return nil end local sx = s:gsub("%s", ""):gsub("<[^>]*>", ""):gsub("%p", "") if sx == "" then return nil else return s end end ------------------------------------------------------------------------------- -- labelorid is a public function to expose the output of labelOrId() -- Pass the Q-number as |qid= or as an unnamed parameter. -- It returns the Wikidata label for that entity or the qid if no label exists. ------------------------------------------------------------------------------- -- Dependencies: labelOrId ------------------------------------------------------------------------------- p.labelorid = function(frame) return (labelOrId(frame.args.qid or frame.args[1])) end ------------------------------------------------------------------------------- -- getLang returns the MediaWiki language code of the current content. -- If optional parameter |style=full, it returns the language name. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getLang = function(frame) local style = (frame.args.style or ""):lower() local langcode = mw.language.getContentLanguage().code if style == "full" then return mw.language.fetchLanguageName( langcode ) end return langcode end ------------------------------------------------------------------------------- -- getItemLangCode takes a qid parameter (using the current page's qid if blank) -- If the item for that qid has property country (P17) it looks at the first preferred value -- If the country has an official language (P37), it looks at the first preferred value -- If that official language has a language code (P424), it returns the first preferred value -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: _getItemLangCode() ------------------------------------------------------------------------------- p.getItemLangCode = function(frame) return _getItemLangCode(frame.args.qid or frame.args[1]) end ------------------------------------------------------------------------------- -- findLanguage exports the local findLang() function -- It takes an optional language code and returns, in order of preference: -- the code if a known language; -- the user's language, if set; -- the server's content language. ------------------------------------------------------------------------------- -- Dependencies: findLang ------------------------------------------------------------------------------- p.findLanguage = function(frame) return findLang(frame.args.lang or frame.args[1]).code end ------------------------------------------------------------------------------- -- getQid returns the qid, if supplied -- failing that, the Wikidata entity ID of the "category's main topic (P301)", if it exists -- failing that, the Wikidata entity ID associated with the current page, if it exists -- otherwise, nothing ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getQid = function(frame) local qid = (frame.args.qid or ""):upper() -- check if a qid was passed; if so, return it: if qid ~= "" then return qid end -- check if there's a "category's main topic (P301)": qid = mw.wikibase.getEntityIdForCurrentPage() if qid then local prop301 = mw.wikibase.getBestStatements(qid, "P301") if prop301[1] then local mctid = prop301[1].mainsnak.datavalue.value.id if mctid then return mctid end end end -- otherwise return the page qid (if any) return qid end ------------------------------------------------------------------------------- -- followQid takes four optional parameters: qid, props, list and all. -- If qid is not given, it uses the qid for the connected page -- or returns nil if there isn't one. -- props is a list of properties, separated by punctuation. -- If props is given, the Wikidata item for the qid is examined for each property in turn. -- If that property contains a value that is another Wikibase-item, that item's qid is returned, -- and the search terminates, unless |all=y when all of the qids are returned, separated by spaces. -- If |list= is set to a template, the qids are passed as arguments to the template. -- If props is not given, the qid is returned. ------------------------------------------------------------------------------- -- Dependencies: parseParam() ------------------------------------------------------------------------------- p._followQid = function(args) local qid = (args.qid or ""):upper() local all = parseParam(args.all, false) local list = args.list or "" if list == "" then list = nil end if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end local out = {} local props = (args.props or ""):upper() if props ~= "" then for p in mw.text.gsplit(props, "%p") do -- split at punctuation and iterate p = mw.text.trim(p) for i, v in ipairs( mw.wikibase.getBestStatements(qid, p) ) do local linkedid = v.mainsnak.datavalue and v.mainsnak.datavalue.value.id if linkedid then if all then out[#out+1] = linkedid else return linkedid end -- test for all or just the first one found end -- test for value exists for that property end -- loop through values of property to follow end -- loop through list of properties to follow end if #out > 0 then local ret = "" if list then ret = mw.getCurrentFrame():expandTemplate{title = list, args = out} else ret = table.concat(out, " ") end return ret else return qid end end p.followQid = function(frame) return p._followQid(frame.args) end ------------------------------------------------------------------------------- -- globalSiteID returns the globalSiteID for the current wiki -- e.g. returns "enwiki" for the English Wikipedia, "enwikisource" for English Wikisource, etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.globalSiteID = function(frame) return mw.wikibase.getGlobalSiteId() end ------------------------------------------------------------------------------- -- siteID returns the root of the globalSiteID -- e.g. "en" for "enwiki", "enwikisource", etc. -- treats "en-gb" as "en", etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.siteID = function(frame) local txtlang = frame:callParserFunction('int', {'lang'}) or "" -- This deals with specific exceptions: be-tarask -> be-x-old if txtlang == "be-tarask" then return "be_x_old" end local pos = txtlang:find("-") local ret = "" if pos then ret = txtlang:sub(1, pos-1) else ret = txtlang end return ret end ------------------------------------------------------------------------------- -- projID returns the code used to link to the reader's language's project -- e.g "en" for [[:en:WikidataIB]] -- treats "en-gb" as "en", etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.projID = function(frame) local txtlang = frame:callParserFunction('int', {'lang'}) or "" -- This deals with specific exceptions: be-tarask -> be-x-old if txtlang == "be-tarask" then return "be-x-old" end local pos = txtlang:find("-") local ret = "" if pos then ret = txtlang:sub(1, pos-1) else ret = txtlang end return ret end ------------------------------------------------------------------------------- -- formatNumber formats a number according to the the supplied language code ("|lang=") -- or the default language if not supplied. -- The number is the first unnamed parameter or "|num=" ------------------------------------------------------------------------------- -- Dependencies: findLang() ------------------------------------------------------------------------------- p.formatNumber = function(frame) local lang local num = tonumber(frame.args[1] or frame.args.num) or 0 lang = findLang(frame.args.lang) return lang:formatNum( num ) end ------------------------------------------------------------------------------- -- examine dumps the property (the unnamed parameter or pid) -- from the item given by the parameter 'qid' (or the other unnamed parameter) -- or from the item corresponding to the current page if qid is not supplied. -- e.g. {{#invoke:WikidataIB |examine |pid=P26 |qid=Q42}} -- or {{#invoke:WikidataIB |examine |P26 |Q42}} or any combination of these -- or {{#invoke:WikidataIB |examine |P26}} for the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.examine = function( frame ) local args if frame.args[1] or frame.args.pid or frame.args.qid then args = frame.args else args = frame:getParent().args end local par = {} local pid = (args.pid or ""):upper() local qid = (args.qid or ""):upper() par[1] = mw.text.trim( args[1] or "" ):upper() par[2] = mw.text.trim( args[2] or "" ):upper() table.sort(par) if par[2]:sub(1,1) == "P" then par[1], par[2] = par[2], par[1] end if pid == "" then pid = par[1] end if qid == "" then qid = par[2] end local q1 = qid:sub(1,1) if pid:sub(1,1) ~= "P" then return "No property supplied" end if q1 ~= "Q" and q1 ~= "M" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return "No item for this page" end return "<pre>" .. mw.dumpObject( mw.wikibase.getAllStatements( qid, pid ) ) .. "</pre>" end ------------------------------------------------------------------------------- -- checkvalue looks for 'val' as a wikibase-item value of a property (the unnamed parameter or pid) -- from the item given by the parameter 'qid' -- or from the Wikidata item associated with the current page if qid is not supplied. -- It only checks ranks that are requested (preferred and normal by default) -- If property is not supplied, then P31 (instance of) is assumed. -- It returns val if found or nothing if not found. -- e.g. {{#invoke:WikidataIB |checkvalue |val=Q5 |pid=P31 |qid=Q42}} -- or {{#invoke:WikidataIB |checkvalue |val=Q5 |P31 |qid=Q42}} -- or {{#invoke:WikidataIB |checkvalue |val=Q5 |qid=Q42}} -- or {{#invoke:WikidataIB |checkvalue |val=Q5 |P31}} for the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.checkvalue = function( frame ) local args if frame.args.val then args = frame.args else args = frame:getParent().args end local val = args.val if not val then return nil end local pid = mw.text.trim(args.pid or args[1] or "P31"):upper() local qid = (args.qid or ""):upper() if pid:sub(1,1) ~= "P" then return nil end if qid:sub(1,1) ~= "Q" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end local ranks = setRanks(args.rank) local stats = {} if ranks.b then stats = mw.wikibase.getBestStatements(qid, pid) else stats = mw.wikibase.getAllStatements( qid, pid ) end if not stats[1] then return nil end if stats[1].mainsnak.datatype == "wikibase-item" then for k, v in pairs( stats ) do local ms = v.mainsnak if ranks[v.rank:sub(1,1)] and ms.snaktype == "value" and ms.datavalue.value.id == val then return val end end end return nil end ------------------------------------------------------------------------------- -- url2 takes a parameter url= that is a proper url and formats it for use in an infobox. -- If no parameter is supplied, it returns nothing. -- This is the equivalent of Template:URL -- but it keeps the "edit at Wikidata" pen icon out of the microformat. -- Usually it will take its url parameter directly from a Wikidata call: -- e.g. {{#invoke:WikidataIB |url2 |url={{wdib |P856 |qid=Q23317 |fwd=ALL |osd=no}} }} ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.url2 = function(frame) local txt = frame.args.url or "" if txt == "" then return nil end -- extract any icon local url, icon = txt:match("(.+)&nbsp;(.+)") -- make sure there's at least a space at the end url = (url or txt) .. " " icon = icon or "" -- extract any protocol like https:// local prot = url:match("(https*://).+[ \"\']") -- extract address local addr = "" if prot then addr = url:match("https*://(.+)[ \"\']") or " " else prot = "//" addr = url:match("[^%p%s]+%.(.+)[ \"\']") or " " end -- strip trailing / from end of domain-only url and add <wbr/> before . and / local disp, n = addr:gsub( "^([^/]+)/$", "%1" ):gsub("%/", "<wbr/>/"):gsub("%.", "<wbr/>.") return '<span class="url">[' .. prot .. addr .. " " .. disp .. "]</span>&nbsp;" .. icon end ------------------------------------------------------------------------------- -- getWebsite fetches the Official website (P856) and formats it for use in an infobox. -- This is similar to Template:Official website but with a url displayed, -- and it adds the "edit at Wikidata" pen icon beyond the microformat if enabled. -- A local value will override the Wikidata value. "NONE" returns nothing. -- e.g. {{#invoke:WikidataIB |getWebsite |qid= |noicon= |lang= |url= }} ------------------------------------------------------------------------------- -- Dependencies: findLang(); parseParam(); ------------------------------------------------------------------------------- p.getWebsite = function(frame) local url = frame.args.url or "" if url:upper() == "NONE" then return nil end local urls = {} local quals = {} local qid = frame.args.qid or "" if url and url ~= "" then urls[1] = url else if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end local prop856 = mw.wikibase.getBestStatements(qid, "P856") for k, v in pairs(prop856) do if v.mainsnak.snaktype == "value" then urls[#urls+1] = v.mainsnak.datavalue.value if v.qualifiers and v.qualifiers["P1065"] then -- just take the first archive url (P1065) local au = v.qualifiers["P1065"][1] if au.snaktype == "value" then quals[#urls] = au.datavalue.value end -- test for archive url having a value end -- test for qualifers end -- test for website having a value end -- loop through website(s) end if #urls == 0 then return nil end local out = {} for i, u in ipairs(urls) do local link = quals[i] or u local prot, addr = u:match("(http[s]*://)(.+)") addr = addr or u local disp, n = addr:gsub("%.", "<wbr/>%.") out[#out+1] = '<span class="url">[' .. link .. " " .. disp .. "]</span>" end local langcode = findLang(frame.args.lang).code local noicon = parseParam(frame.args.noicon, false) if url == "" and not noicon then out[#out] = out[#out] .. createicon(langcode, qid, "P856") end local ret = "" if #out > 1 then ret = mw.getCurrentFrame():expandTemplate{title = "ubl", args = out} else ret = out[1] end return ret end ------------------------------------------------------------------------------- -- getAllLabels fetches the set of labels and formats it for display as wikitext. -- It takes a parameter 'qid' for arbitrary access, otherwise it uses the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAllLabels = function(frame) local args = frame.args or frame:getParent().args or {} local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return i18n["entity-not-found"] end local labels = mw.wikibase.getEntity(qid).labels if not labels then return i18n["labels-not-found"] end local out = {} for k, v in pairs(labels) do out[#out+1] = v.value .. " (" .. v.language .. ")" end return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- getAllDescriptions fetches the set of descriptions and formats it for display as wikitext. -- It takes a parameter 'qid' for arbitrary access, otherwise it uses the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAllDescriptions = function(frame) local args = frame.args or frame:getParent().args or {} local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return i18n["entity-not-found"] end local descriptions = mw.wikibase.getEntity(qid).descriptions if not descriptions then return i18n["descriptions-not-found"] end local out = {} for k, v in pairs(descriptions) do out[#out+1] = v.value .. " (" .. v.language .. ")" end return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- getAllAliases fetches the set of aliases and formats it for display as wikitext. -- It takes a parameter 'qid' for arbitrary access, otherwise it uses the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAllAliases = function(frame) local args = frame.args or frame:getParent().args or {} local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return i18n["entity-not-found"] end local aliases = mw.wikibase.getEntity(qid).aliases if not aliases then return i18n["aliases-not-found"] end local out = {} for k1, v1 in pairs(aliases) do local lang = v1[1].language local val = {} for k1, v2 in ipairs(v1) do val[#val+1] = v2.value end out[#out+1] = table.concat(val, ", ") .. " (" .. lang .. ")" end return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- showNoLinks displays the article titles that should not be linked. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.showNoLinks = function(frame) local out = {} for k, v in pairs(donotlink) do out[#out+1] = k end table.sort( out ) return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- checkValidity checks whether the first unnamed parameter represents a valid entity-id, -- that is, something like Q1235 or P123. -- It returns the strings "true" or "false". -- Change false to nil to return "true" or "" (easier to test with #if:). ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- function p.checkValidity(frame) local id = mw.text.trim(frame.args[1] or "") if mw.wikibase.isValidEntityId(id) then return true else return false end end ------------------------------------------------------------------------------- -- getEntityFromTitle returns the Entity-ID (Q-number) for a given title. -- Modification of Module:ResolveEntityId -- The title is the first unnamed parameter. -- The site parameter determines the site/language for the title. Defaults to current wiki. -- The showdab parameter determines whether dab pages should return the Q-number or nil. Defaults to true. -- Returns the Q-number or nil if it does not exist. ------------------------------------------------------------------------------- -- Dependencies: parseParam ------------------------------------------------------------------------------- function p.getEntityFromTitle(frame) local args=frame.args if not args[1] then args=frame:getParent().args end if not args[1] then return nil end local title = mw.text.trim(args[1]) local site = args.site or "" local showdab = parseParam(args.showdab, true) local qid = mw.wikibase.getEntityIdForTitle(title, site) if qid then local prop31 = mw.wikibase.getBestStatements(qid, "P31")[1] if not showdab and prop31 and prop31.mainsnak.datavalue.value.id == "Q4167410" then return nil else return qid end end end ------------------------------------------------------------------------------- -- getDatePrecision returns the number representing the precision of the first best date value -- for the given property. -- It takes the qid and property ID -- The meanings are given at https://www.mediawiki.org/wiki/Wikibase/DataModel#Dates_and_times -- 0 = 1 billion years .. 6 = millennium, 7 = century, 8 = decade, 9 = year, 10 = month, 11 = day -- Returns 0 (or the second unnamed parameter) if the Wikidata does not exist. ------------------------------------------------------------------------------- -- Dependencies: parseParam; sourced; ------------------------------------------------------------------------------- function p.getDatePrecision(frame) local args=frame.args if not args[1] then args=frame:getParent().args end local default = tonumber(args[2] or args.default) or 0 local prop = mw.text.trim(args[1] or "") if prop == "" then return default end local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return default end local onlysrc = parseParam(args.onlysourced or args.osd, true) local stat = mw.wikibase.getBestStatements(qid, prop) for i, v in ipairs(stat) do local prec = (onlysrc == false or sourced(v)) and v.mainsnak.datavalue and v.mainsnak.datavalue.value and v.mainsnak.datavalue.value.precision if prec then return prec end end return default end return p ------------------------------------------------------------------------------- -- List of exported functions ------------------------------------------------------------------------------- --[[ _getValue getValue getPreferredValue getCoords getQualifierValue getSumOfParts getValueByQual getValueByLang getValueByRefSource getPropertyIDs getQualifierIDs getPropOfProp getAwardCat getIntersectCat getGlobe getCommonsLink getSiteLink getLink getLabel label getAT getDescription getAliases pageId formatDate location checkBlacklist emptyor labelorid getLang getItemLangCode findLanguage getQID followQid globalSiteID siteID projID formatNumber examine checkvalue url2 getWebsite getAllLabels getAllDescriptions getAllAliases showNoLinks checkValidity getEntityFromTitle getDatePrecision --]] ------------------------------------------------------------------------------- 24d1c7c6e71b14f4fc20ca1b4ea2301469bea627 Module:Documentation 828 344 682 2023-06-17T01:23:52Z wikipedia>Izno 0 per tper Scribunto text/plain -- This module implements {{documentation}}. -- Get required modules. local getArgs = require('Module:Arguments').getArgs -- Get the config table. local cfg = mw.loadData('Module:Documentation/config') local p = {} -- Often-used functions. local ugsub = mw.ustring.gsub ---------------------------------------------------------------------------- -- Helper functions -- -- These are defined as local functions, but are made available in the p -- table for testing purposes. ---------------------------------------------------------------------------- local function message(cfgKey, valArray, expectType) --[[ -- Gets a message from the cfg table and formats it if appropriate. -- The function raises an error if the value from the cfg table is not -- of the type expectType. The default type for expectType is 'string'. -- If the table valArray is present, strings such as $1, $2 etc. in the -- message are substituted with values from the table keys [1], [2] etc. -- For example, if the message "foo-message" had the value 'Foo $2 bar $1.', -- message('foo-message', {'baz', 'qux'}) would return "Foo qux bar baz." --]] local msg = cfg[cfgKey] expectType = expectType or 'string' if type(msg) ~= expectType then error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2) end if not valArray then return msg end local function getMessageVal(match) match = tonumber(match) return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4) end return ugsub(msg, '$([1-9][0-9]*)', getMessageVal) end p.message = message local function makeWikilink(page, display) if display then return mw.ustring.format('[[%s|%s]]', page, display) else return mw.ustring.format('[[%s]]', page) end end p.makeWikilink = makeWikilink local function makeCategoryLink(cat, sort) local catns = mw.site.namespaces[14].name return makeWikilink(catns .. ':' .. cat, sort) end p.makeCategoryLink = makeCategoryLink local function makeUrlLink(url, display) return mw.ustring.format('[%s %s]', url, display) end p.makeUrlLink = makeUrlLink local function makeToolbar(...) local ret = {} local lim = select('#', ...) if lim < 1 then return nil end for i = 1, lim do ret[#ret + 1] = select(i, ...) end -- 'documentation-toolbar' return '<span class="' .. message('toolbar-class') .. '">(' .. table.concat(ret, ' &#124; ') .. ')</span>' end p.makeToolbar = makeToolbar ---------------------------------------------------------------------------- -- Argument processing ---------------------------------------------------------------------------- local function makeInvokeFunc(funcName) return function (frame) local args = getArgs(frame, { valueFunc = function (key, value) if type(value) == 'string' then value = value:match('^%s*(.-)%s*$') -- Remove whitespace. if key == 'heading' or value ~= '' then return value else return nil end else return value end end }) return p[funcName](args) end end ---------------------------------------------------------------------------- -- Entry points ---------------------------------------------------------------------------- function p.nonexistent(frame) if mw.title.getCurrentTitle().subpageText == 'testcases' then return frame:expandTemplate{title = 'module test cases notice'} else return p.main(frame) end end p.main = makeInvokeFunc('_main') function p._main(args) --[[ -- This function defines logic flow for the module. -- @args - table of arguments passed by the user --]] local env = p.getEnvironment(args) local root = mw.html.create() root :wikitext(p._getModuleWikitext(args, env)) :wikitext(p.protectionTemplate(env)) :wikitext(p.sandboxNotice(args, env)) :tag('div') -- 'documentation-container' :addClass(message('container')) :attr('role', 'complementary') :attr('aria-labelledby', args.heading ~= '' and 'documentation-heading' or nil) :attr('aria-label', args.heading == '' and 'Documentation' or nil) :newline() :tag('div') -- 'documentation' :addClass(message('main-div-classes')) :newline() :wikitext(p._startBox(args, env)) :wikitext(p._content(args, env)) :tag('div') -- 'documentation-clear' :addClass(message('clear')) :done() :newline() :done() :wikitext(p._endBox(args, env)) :done() :wikitext(p.addTrackingCategories(env)) -- 'Module:Documentation/styles.css' return mw.getCurrentFrame():extensionTag ( 'templatestyles', '', {src=cfg['templatestyles'] }) .. tostring(root) end ---------------------------------------------------------------------------- -- Environment settings ---------------------------------------------------------------------------- function p.getEnvironment(args) --[[ -- Returns a table with information about the environment, including title -- objects and other namespace- or path-related data. -- @args - table of arguments passed by the user -- -- Title objects include: -- env.title - the page we are making documentation for (usually the current title) -- env.templateTitle - the template (or module, file, etc.) -- env.docTitle - the /doc subpage. -- env.sandboxTitle - the /sandbox subpage. -- env.testcasesTitle - the /testcases subpage. -- -- Data includes: -- env.protectionLevels - the protection levels table of the title object. -- env.subjectSpace - the number of the title's subject namespace. -- env.docSpace - the number of the namespace the title puts its documentation in. -- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace. -- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template. -- -- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value -- returned will be nil. --]] local env, envFuncs = {}, {} -- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value -- returned by that function is memoized in the env table so that we don't call any of the functions -- more than once. (Nils won't be memoized.) setmetatable(env, { __index = function (t, key) local envFunc = envFuncs[key] if envFunc then local success, val = pcall(envFunc) if success then env[key] = val -- Memoise the value. return val end end return nil end }) function envFuncs.title() -- The title object for the current page, or a test page passed with args.page. local title local titleArg = args.page if titleArg then title = mw.title.new(titleArg) else title = mw.title.getCurrentTitle() end return title end function envFuncs.templateTitle() --[[ -- The template (or module, etc.) title object. -- Messages: -- 'sandbox-subpage' --> 'sandbox' -- 'testcases-subpage' --> 'testcases' --]] local subjectSpace = env.subjectSpace local title = env.title local subpage = title.subpageText if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then return mw.title.makeTitle(subjectSpace, title.baseText) else return mw.title.makeTitle(subjectSpace, title.text) end end function envFuncs.docTitle() --[[ -- Title object of the /doc subpage. -- Messages: -- 'doc-subpage' --> 'doc' --]] local title = env.title local docname = args[1] -- User-specified doc page. local docpage if docname then docpage = docname else docpage = env.docpageBase .. '/' .. message('doc-subpage') end return mw.title.new(docpage) end function envFuncs.sandboxTitle() --[[ -- Title object for the /sandbox subpage. -- Messages: -- 'sandbox-subpage' --> 'sandbox' --]] return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage')) end function envFuncs.testcasesTitle() --[[ -- Title object for the /testcases subpage. -- Messages: -- 'testcases-subpage' --> 'testcases' --]] return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage')) end function envFuncs.protectionLevels() -- The protection levels table of the title object. return env.title.protectionLevels end function envFuncs.subjectSpace() -- The subject namespace number. return mw.site.namespaces[env.title.namespace].subject.id end function envFuncs.docSpace() -- The documentation namespace number. For most namespaces this is the -- same as the subject namespace. However, pages in the Article, File, -- MediaWiki or Category namespaces must have their /doc, /sandbox and -- /testcases pages in talk space. local subjectSpace = env.subjectSpace if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then return subjectSpace + 1 else return subjectSpace end end function envFuncs.docpageBase() -- The base page of the /doc, /sandbox, and /testcases subpages. -- For some namespaces this is the talk page, rather than the template page. local templateTitle = env.templateTitle local docSpace = env.docSpace local docSpaceText = mw.site.namespaces[docSpace].name -- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon. return docSpaceText .. ':' .. templateTitle.text end function envFuncs.compareUrl() -- Diff link between the sandbox and the main template using [[Special:ComparePages]]. local templateTitle = env.templateTitle local sandboxTitle = env.sandboxTitle if templateTitle.exists and sandboxTitle.exists then local compareUrl = mw.uri.fullUrl( 'Special:ComparePages', { page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText} ) return tostring(compareUrl) else return nil end end return env end ---------------------------------------------------------------------------- -- Auxiliary templates ---------------------------------------------------------------------------- p.getModuleWikitext = makeInvokeFunc('_getModuleWikitext') function p._getModuleWikitext(args, env) local currentTitle = mw.title.getCurrentTitle() if currentTitle.contentModel ~= 'Scribunto' then return end pcall(require, currentTitle.prefixedText) -- if it fails, we don't care local moduleWikitext = package.loaded["Module:Module wikitext"] if moduleWikitext then return moduleWikitext.main() end end function p.sandboxNotice(args, env) --[=[ -- Generates a sandbox notice for display above sandbox pages. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'sandbox-notice-image' --> '[[File:Sandbox.svg|50px|alt=|link=]]' -- 'sandbox-notice-blurb' --> 'This is the $1 for $2.' -- 'sandbox-notice-diff-blurb' --> 'This is the $1 for $2 ($3).' -- 'sandbox-notice-pagetype-template' --> '[[Wikipedia:Template test cases|template sandbox]] page' -- 'sandbox-notice-pagetype-module' --> '[[Wikipedia:Template test cases|module sandbox]] page' -- 'sandbox-notice-pagetype-other' --> 'sandbox page' -- 'sandbox-notice-compare-link-display' --> 'diff' -- 'sandbox-notice-testcases-blurb' --> 'See also the companion subpage for $1.' -- 'sandbox-notice-testcases-link-display' --> 'test cases' -- 'sandbox-category' --> 'Template sandboxes' --]=] local title = env.title local sandboxTitle = env.sandboxTitle local templateTitle = env.templateTitle local subjectSpace = env.subjectSpace if not (subjectSpace and title and sandboxTitle and templateTitle and mw.title.equals(title, sandboxTitle)) then return nil end -- Build the table of arguments to pass to {{ombox}}. We need just two fields, "image" and "text". local omargs = {} omargs.image = message('sandbox-notice-image') -- Get the text. We start with the opening blurb, which is something like -- "This is the template sandbox for [[Template:Foo]] (diff)." local text = '' local pagetype if subjectSpace == 10 then pagetype = message('sandbox-notice-pagetype-template') elseif subjectSpace == 828 then pagetype = message('sandbox-notice-pagetype-module') else pagetype = message('sandbox-notice-pagetype-other') end local templateLink = makeWikilink(templateTitle.prefixedText) local compareUrl = env.compareUrl if compareUrl then local compareDisplay = message('sandbox-notice-compare-link-display') local compareLink = makeUrlLink(compareUrl, compareDisplay) text = text .. message('sandbox-notice-diff-blurb', {pagetype, templateLink, compareLink}) else text = text .. message('sandbox-notice-blurb', {pagetype, templateLink}) end -- Get the test cases page blurb if the page exists. This is something like -- "See also the companion subpage for [[Template:Foo/testcases|test cases]]." local testcasesTitle = env.testcasesTitle if testcasesTitle and testcasesTitle.exists then if testcasesTitle.contentModel == "Scribunto" then local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display') local testcasesRunLinkDisplay = message('sandbox-notice-testcases-run-link-display') local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay) local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay) text = text .. '<br />' .. message('sandbox-notice-testcases-run-blurb', {testcasesLink, testcasesRunLink}) else local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display') local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay) text = text .. '<br />' .. message('sandbox-notice-testcases-blurb', {testcasesLink}) end end -- Add the sandbox to the sandbox category. omargs.text = text .. makeCategoryLink(message('sandbox-category')) -- 'documentation-clear' return '<div class="' .. message('clear') .. '"></div>' .. require('Module:Message box').main('ombox', omargs) end function p.protectionTemplate(env) -- Generates the padlock icon in the top right. -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'protection-template' --> 'pp-template' -- 'protection-template-args' --> {docusage = 'yes'} local protectionLevels = env.protectionLevels if not protectionLevels then return nil end local editProt = protectionLevels.edit and protectionLevels.edit[1] local moveProt = protectionLevels.move and protectionLevels.move[1] if editProt then -- The page is edit-protected. return require('Module:Protection banner')._main{ message('protection-reason-edit'), small = true } elseif moveProt and moveProt ~= 'autoconfirmed' then -- The page is move-protected but not edit-protected. Exclude move -- protection with the level "autoconfirmed", as this is equivalent to -- no move protection at all. return require('Module:Protection banner')._main{ action = 'move', small = true } else return nil end end ---------------------------------------------------------------------------- -- Start box ---------------------------------------------------------------------------- p.startBox = makeInvokeFunc('_startBox') function p._startBox(args, env) --[[ -- This function generates the start box. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make -- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox -- which generate the box HTML. --]] env = env or p.getEnvironment(args) local links local content = args.content if not content or args[1] then -- No need to include the links if the documentation is on the template page itself. local linksData = p.makeStartBoxLinksData(args, env) if linksData then links = p.renderStartBoxLinks(linksData) end end -- Generate the start box html. local data = p.makeStartBoxData(args, env, links) if data then return p.renderStartBox(data) else -- User specified no heading. return nil end end function p.makeStartBoxLinksData(args, env) --[[ -- Does initial processing of data to make the [view] [edit] [history] [purge] links. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'view-link-display' --> 'view' -- 'edit-link-display' --> 'edit' -- 'history-link-display' --> 'history' -- 'purge-link-display' --> 'purge' -- 'module-preload' --> 'Template:Documentation/preload-module-doc' -- 'docpage-preload' --> 'Template:Documentation/preload' -- 'create-link-display' --> 'create' --]] local subjectSpace = env.subjectSpace local title = env.title local docTitle = env.docTitle if not title or not docTitle then return nil end if docTitle.isRedirect then docTitle = docTitle.redirectTarget end local data = {} data.title = title data.docTitle = docTitle -- View, display, edit, and purge links if /doc exists. data.viewLinkDisplay = message('view-link-display') data.editLinkDisplay = message('edit-link-display') data.historyLinkDisplay = message('history-link-display') data.purgeLinkDisplay = message('purge-link-display') -- Create link if /doc doesn't exist. local preload = args.preload if not preload then if subjectSpace == 828 then -- Module namespace preload = message('module-preload') else preload = message('docpage-preload') end end data.preload = preload data.createLinkDisplay = message('create-link-display') return data end function p.renderStartBoxLinks(data) --[[ -- Generates the [view][edit][history][purge] or [create][purge] links from the data table. -- @data - a table of data generated by p.makeStartBoxLinksData --]] local docTitle = data.docTitle local purgeLink = makeWikilink("Special:Purge/" .. docTitle.prefixedText, data.purgeLinkDisplay) if docTitle.exists then local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay) local editLink = makeWikilink("Special:EditPage/" .. docTitle.prefixedText, data.editLinkDisplay) local historyLink = makeWikilink("Special:PageHistory/" .. docTitle.prefixedText, data.historyLinkDisplay) return "&#91;" .. viewLink .. "&#93; &#91;" .. editLink .. "&#93; &#91;" .. historyLink .. "&#93; &#91;" .. purgeLink .. "&#93;" else local createLink = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay) return "&#91;" .. createLink .. "&#93; &#91;" .. purgeLink .. "&#93;" end return ret end function p.makeStartBoxData(args, env, links) --[=[ -- Does initial processing of data to pass to the start-box render function, p.renderStartBox. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- @links - a string containing the [view][edit][history][purge] links - could be nil if there's an error. -- -- Messages: -- 'documentation-icon-wikitext' --> '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]' -- 'template-namespace-heading' --> 'Template documentation' -- 'module-namespace-heading' --> 'Module documentation' -- 'file-namespace-heading' --> 'Summary' -- 'other-namespaces-heading' --> 'Documentation' -- 'testcases-create-link-display' --> 'create' --]=] local subjectSpace = env.subjectSpace if not subjectSpace then -- Default to an "other namespaces" namespace, so that we get at least some output -- if an error occurs. subjectSpace = 2 end local data = {} -- Heading local heading = args.heading -- Blank values are not removed. if heading == '' then -- Don't display the start box if the heading arg is defined but blank. return nil end if heading then data.heading = heading elseif subjectSpace == 10 then -- Template namespace data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading') elseif subjectSpace == 828 then -- Module namespace data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading') elseif subjectSpace == 6 then -- File namespace data.heading = message('file-namespace-heading') else data.heading = message('other-namespaces-heading') end -- Heading CSS local headingStyle = args['heading-style'] if headingStyle then data.headingStyleText = headingStyle else -- 'documentation-heading' data.headingClass = message('main-div-heading-class') end -- Data for the [view][edit][history][purge] or [create] links. if links then -- 'mw-editsection-like plainlinks' data.linksClass = message('start-box-link-classes') data.links = links end return data end function p.renderStartBox(data) -- Renders the start box html. -- @data - a table of data generated by p.makeStartBoxData. local sbox = mw.html.create('div') sbox -- 'documentation-startbox' :addClass(message('start-box-class')) :newline() :tag('span') :addClass(data.headingClass) :attr('id', 'documentation-heading') :cssText(data.headingStyleText) :wikitext(data.heading) local links = data.links if links then sbox:tag('span') :addClass(data.linksClass) :attr('id', data.linksId) :wikitext(links) end return tostring(sbox) end ---------------------------------------------------------------------------- -- Documentation content ---------------------------------------------------------------------------- p.content = makeInvokeFunc('_content') function p._content(args, env) -- Displays the documentation contents -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment env = env or p.getEnvironment(args) local docTitle = env.docTitle local content = args.content if not content and docTitle and docTitle.exists then content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText} end -- The line breaks below are necessary so that "=== Headings ===" at the start and end -- of docs are interpreted correctly. return '\n' .. (content or '') .. '\n' end p.contentTitle = makeInvokeFunc('_contentTitle') function p._contentTitle(args, env) env = env or p.getEnvironment(args) local docTitle = env.docTitle if not args.content and docTitle and docTitle.exists then return docTitle.prefixedText else return '' end end ---------------------------------------------------------------------------- -- End box ---------------------------------------------------------------------------- p.endBox = makeInvokeFunc('_endBox') function p._endBox(args, env) --[=[ -- This function generates the end box (also known as the link box). -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- --]=] -- Get environment data. env = env or p.getEnvironment(args) local subjectSpace = env.subjectSpace local docTitle = env.docTitle if not subjectSpace or not docTitle then return nil end -- Check whether we should output the end box at all. Add the end -- box by default if the documentation exists or if we are in the -- user, module or template namespaces. local linkBox = args['link box'] if linkBox == 'off' or not ( docTitle.exists or subjectSpace == 2 or subjectSpace == 828 or subjectSpace == 10 ) then return nil end -- Assemble the link box. local text = '' if linkBox then text = text .. linkBox else text = text .. (p.makeDocPageBlurb(args, env) or '') -- "This documentation is transcluded from [[Foo]]." if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then -- We are in the user, template or module namespaces. -- Add sandbox and testcases links. -- "Editors can experiment in this template's sandbox and testcases pages." text = text .. (p.makeExperimentBlurb(args, env) or '') .. '<br />' if not args.content and not args[1] then -- "Please add categories to the /doc subpage." -- Don't show this message with inline docs or with an explicitly specified doc page, -- as then it is unclear where to add the categories. text = text .. (p.makeCategoriesBlurb(args, env) or '') end text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '') --"Subpages of this template" end end local box = mw.html.create('div') -- 'documentation-metadata' box:attr('role', 'note') :addClass(message('end-box-class')) -- 'plainlinks' :addClass(message('end-box-plainlinks')) :wikitext(text) :done() return '\n' .. tostring(box) end function p.makeDocPageBlurb(args, env) --[=[ -- Makes the blurb "This documentation is transcluded from [[Template:Foo]] (edit, history)". -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'edit-link-display' --> 'edit' -- 'history-link-display' --> 'history' -- 'transcluded-from-blurb' --> -- 'The above [[Wikipedia:Template documentation|documentation]] -- is [[Help:Transclusion|transcluded]] from $1.' -- 'module-preload' --> 'Template:Documentation/preload-module-doc' -- 'create-link-display' --> 'create' -- 'create-module-doc-blurb' --> -- 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].' --]=] local docTitle = env.docTitle if not docTitle then return nil end local ret if docTitle.exists then -- /doc exists; link to it. local docLink = makeWikilink(docTitle.prefixedText) local editDisplay = message('edit-link-display') local editLink = makeWikilink("Special:EditPage/" .. docTitle.prefixedText, editDisplay) local historyDisplay = message('history-link-display') local historyLink = makeWikilink("Special:PageHistory/" .. docTitle.prefixedText, historyDisplay) ret = message('transcluded-from-blurb', {docLink}) .. ' ' .. makeToolbar(editLink, historyLink) .. '<br />' elseif env.subjectSpace == 828 then -- /doc does not exist; ask to create it. local createUrl = docTitle:fullUrl{action = 'edit', preload = message('module-preload')} local createDisplay = message('create-link-display') local createLink = makeUrlLink(createUrl, createDisplay) ret = message('create-module-doc-blurb', {createLink}) .. '<br />' end return ret end function p.makeExperimentBlurb(args, env) --[[ -- Renders the text "Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages." -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'sandbox-link-display' --> 'sandbox' -- 'sandbox-edit-link-display' --> 'edit' -- 'compare-link-display' --> 'diff' -- 'module-sandbox-preload' --> 'Template:Documentation/preload-module-sandbox' -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox' -- 'sandbox-create-link-display' --> 'create' -- 'mirror-edit-summary' --> 'Create sandbox version of $1' -- 'mirror-link-display' --> 'mirror' -- 'mirror-link-preload' --> 'Template:Documentation/mirror' -- 'sandbox-link-display' --> 'sandbox' -- 'testcases-link-display' --> 'testcases' -- 'testcases-edit-link-display'--> 'edit' -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox' -- 'testcases-create-link-display' --> 'create' -- 'testcases-link-display' --> 'testcases' -- 'testcases-edit-link-display' --> 'edit' -- 'module-testcases-preload' --> 'Template:Documentation/preload-module-testcases' -- 'template-testcases-preload' --> 'Template:Documentation/preload-testcases' -- 'experiment-blurb-module' --> 'Editors can experiment in this module's $1 and $2 pages.' -- 'experiment-blurb-template' --> 'Editors can experiment in this template's $1 and $2 pages.' --]] local subjectSpace = env.subjectSpace local templateTitle = env.templateTitle local sandboxTitle = env.sandboxTitle local testcasesTitle = env.testcasesTitle local templatePage = templateTitle.prefixedText if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then return nil end -- Make links. local sandboxLinks, testcasesLinks if sandboxTitle.exists then local sandboxPage = sandboxTitle.prefixedText local sandboxDisplay = message('sandbox-link-display') local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay) local sandboxEditDisplay = message('sandbox-edit-link-display') local sandboxEditLink = makeWikilink("Special:EditPage/" .. sandboxPage, sandboxEditDisplay) local compareUrl = env.compareUrl local compareLink if compareUrl then local compareDisplay = message('compare-link-display') compareLink = makeUrlLink(compareUrl, compareDisplay) end sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink) else local sandboxPreload if subjectSpace == 828 then sandboxPreload = message('module-sandbox-preload') else sandboxPreload = message('template-sandbox-preload') end local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload} local sandboxCreateDisplay = message('sandbox-create-link-display') local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay) local mirrorSummary = message('mirror-edit-summary', {makeWikilink(templatePage)}) local mirrorPreload = message('mirror-link-preload') local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = mirrorPreload, summary = mirrorSummary} if subjectSpace == 828 then mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templateTitle.prefixedText, summary = mirrorSummary} end local mirrorDisplay = message('mirror-link-display') local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay) sandboxLinks = message('sandbox-link-display') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink) end if testcasesTitle.exists then local testcasesPage = testcasesTitle.prefixedText local testcasesDisplay = message('testcases-link-display') local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay) local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'} local testcasesEditDisplay = message('testcases-edit-link-display') local testcasesEditLink = makeWikilink("Special:EditPage/" .. testcasesPage, testcasesEditDisplay) -- for Modules, add testcases run link if exists if testcasesTitle.contentModel == "Scribunto" and testcasesTitle.talkPageTitle and testcasesTitle.talkPageTitle.exists then local testcasesRunLinkDisplay = message('testcases-run-link-display') local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay) testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink, testcasesRunLink) else testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink) end else local testcasesPreload if subjectSpace == 828 then testcasesPreload = message('module-testcases-preload') else testcasesPreload = message('template-testcases-preload') end local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload} local testcasesCreateDisplay = message('testcases-create-link-display') local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay) testcasesLinks = message('testcases-link-display') .. ' ' .. makeToolbar(testcasesCreateLink) end local messageName if subjectSpace == 828 then messageName = 'experiment-blurb-module' else messageName = 'experiment-blurb-template' end return message(messageName, {sandboxLinks, testcasesLinks}) end function p.makeCategoriesBlurb(args, env) --[[ -- Generates the text "Please add categories to the /doc subpage." -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'doc-link-display' --> '/doc' -- 'add-categories-blurb' --> 'Please add categories to the $1 subpage.' --]] local docTitle = env.docTitle if not docTitle then return nil end local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display')) return message('add-categories-blurb', {docPathLink}) end function p.makeSubpagesBlurb(args, env) --[[ -- Generates the "Subpages of this template" link. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'template-pagetype' --> 'template' -- 'module-pagetype' --> 'module' -- 'default-pagetype' --> 'page' -- 'subpages-link-display' --> 'Subpages of this $1' --]] local subjectSpace = env.subjectSpace local templateTitle = env.templateTitle if not subjectSpace or not templateTitle then return nil end local pagetype if subjectSpace == 10 then pagetype = message('template-pagetype') elseif subjectSpace == 828 then pagetype = message('module-pagetype') else pagetype = message('default-pagetype') end local subpagesLink = makeWikilink( 'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/', message('subpages-link-display', {pagetype}) ) return message('subpages-blurb', {subpagesLink}) end ---------------------------------------------------------------------------- -- Tracking categories ---------------------------------------------------------------------------- function p.addTrackingCategories(env) --[[ -- Check if {{documentation}} is transcluded on a /doc or /testcases page. -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'display-strange-usage-category' --> true -- 'doc-subpage' --> 'doc' -- 'testcases-subpage' --> 'testcases' -- 'strange-usage-category' --> 'Wikipedia pages with strange ((documentation)) usage' -- -- /testcases pages in the module namespace are not categorised, as they may have -- {{documentation}} transcluded automatically. --]] local title = env.title local subjectSpace = env.subjectSpace if not title or not subjectSpace then return nil end local subpage = title.subpageText local ret = '' if message('display-strange-usage-category', nil, 'boolean') and ( subpage == message('doc-subpage') or subjectSpace ~= 828 and subpage == message('testcases-subpage') ) then ret = ret .. makeCategoryLink(message('strange-usage-category')) end return ret end return p 728908c27cb1e65ac402f1af8a9e4f144ddc3d0d Module:Find sources/links 828 429 858 2023-06-21T18:45:56Z wikipedia>Izno 0 [[phab:T337149#8953569]] Scribunto text/plain -- This is a list of links used by [[Module:Find sources]]. return { ["archive.org"] = { url = 'https://archive.org/search.php?query=$1%20AND%20mediatype:texts', display = 'Archive.org', description = "The [[Internet Archive]], a digital library of public websites.", }, ["bing"] = { url = 'https://www.bing.com/search?q=$1', display = 'Bing', description = "[[Bing (search engine)|Bing]], Microsoft's flagship search engine.", }, ["britannica"] = { url = 'https://www.britannica.com/search?nop=1a15&cse=on&query=$1&cx=ccef96e8363da4b5f&tbm=3&fxx=3', display = 'Encyclopedia Britannica', description = "[[Encyclopedia Britannica]]", }, ["british library"] = { url = 'https://explore.bl.uk/primo_library/libweb/action/search.do?fn=search&ct=search&initialSearch=true&mode=Basic&tab=local_tab&indx=1&dum=true&srt=rank&vid=BLVU1&frbg=&tb=t&vl%28freeText0%29=$1', display = 'British Library', description = "National library of the United Kingdom", }, ["ccsearch"] = { url = "https://search.creativecommons.org/search?q=$1&license_type=commercial,modification", display = "CC Search", description = "CC Search: The official search engine of [[Creative Commons]]", }, ["cochrane"] = { url = "https://www.cochranelibrary.com/en/advanced-search?searchBy=-1&isWordVariations=&resultPerPage=25&searchType=advanced&selectedType=review&displayText=&orderBy=relevancy&p_p_id=scolarissearchresultsportlet_WAR_scolarissearchresults&p_p_lifecycle=0&p_p_state=normal&p_p_mode=view&p_p_col_id=column-1&p_p_col_pos=1&p_p_col_count=2&searchText=$1", display = "Cochrane", description = "[[Cochrane Library]]: Leading publisher of systematic reviews.", }, ["doaj"] = { url = "https://www.doaj.org/search/articles?source=%7B%22query%22%3A%7B%22query_string%22%3A%7B%22query%22%3A$1%2C%22default_operator%22%3A%22AND%22%7D%7D%7D", display = "DOAJ", description = "[[Directory of Open Access Journals|DOAJ]]: Directory of Open Access Journals", }, ["duckduckgo"] = { url = 'https://duckduckgo.com/?q=$1', display = 'DuckDuckGo', description = "[[DuckDuckGo]], a search engine that emphasizes protecting searchers' privacy and avoiding the \"filter bubble\" of personalized search results.", }, ["eowb"] = { url = 'https://search.lib.umich.edu/everything?query=$1', display = 'Encyclopedia of World Biography', tooltip = 'Encyclopedia of World Biography, by Gale Research; online results provided by University of Michigan.', }, ["free news sources"] = { url = 'https://en.wikipedia.org/wiki/Wikipedia:Free_English_newspaper_sources', display = 'free news sources', }, ["gale"] = { url = "https://go.gale.com/ps/basicSearch.do?inputFieldNames%5B0%5D=OQE&nwf=y&searchType=BasicSearchForm&userGroupName=anon%7Ee3e4f4eb&prodId=AONE&spellCheck=true&method=doSearch&dblist=&stw.option=&ebook=&singleLimiterFieldValues%5BAC%5D=y&_singleLimiterFieldValues%5BAC%5D=on&_singleLimiterFieldValues%5BRE%5D=on&standAloneLimiters=LI&_singleLimiterFieldValues%5BLI%5D=on&inputFieldValues%5B0%5D=$1", display = "Gale Academic OneFile", description = " Gale Academic OneFile: Academic publisher portal from [[Gale (publisher)|Gale]]. Access via [[Wikipedia:The Wikipedia Library|Wikipedia Library]]", }, ["gin"] = { url = "https://guidelines.ebmportal.com/guidelines-international-network?type=search&search=$1", display = "GIN guidelines", tooltip = "Content from the library and the registry of guidelines in development provided by the Guidelines International Network", }, ["globe and mail"] = { url = 'https://www.theglobeandmail.com/search/?q=$1', display = "''The Globe and Mail''", description = "The website of ''[[The Globe and Mail]]'', a [[newspaper of record]] for Canada.", }, ["google"] = { url = 'https://www.google.com/search?as_eq=wikipedia&q=$1', display = 'Google', description = "[[Google]], the flagship search engine from Google Inc.", }, ["google books"] = { url = 'https://www.google.com/search?tbs=bks:1&q=$1+-wikipedia', display = 'Google Books', description = "[[Google Books]], Google's search engine for books." }, ["google free images"] = { url = 'https://www.google.com/search?safe=off&tbs=sur:fmc&tbm=isch&q=$1+-site:wikipedia.org+-site:wikimedia.org', display = 'Free Google Images', description = "[[Google Images]], Google's search engine for images. Only images compatible with Wikipedia's licensing are included.", }, ["google news"] = { url = 'https://www.google.com/search?tbm=nws&q=$1+-wikipedia&tbs=ar:1', display = 'Google News', description = "[[Google News]], Google's search engine for news sites.", notes = "In the past this link searched news archives, but this functionality has been removed by Google. Currently, only recent news articles are searched.", }, ["google newspapers"] = { url = 'https://www.google.com/search?&q=$1&tbs=bkt:s&tbm=bks', display = 'Google Newspapers', description = "Google Newspapers, a search of Google's digital archive of print newspapers.", }, ["google scholar"] = { url = 'https://scholar.google.com/scholar?q=$1', display = 'Google Scholar', description = "[[Google Scholar]], Google's search engine for academic papers and other scholarly research.", }, ["haaretz"] = { url = 'https://www.haaretz.com/search-results?q=$1', display = "''Haaretz''", description = "The website of ''[[Haaretz]]'', a [[newspaper of record]] for Israel.", }, ["internet archive scholar"] = { url = 'https://scholar.archive.org/search?&sort_order=time_desc&q=$1', display = 'IA scholar', description = "The [[Internet Archive Scholar]], a digital library of open access academic journals.", tooltip = "The [[Internet Archive Scholar]], a digital library of open access academic journals.", }, ["infoplease"] = { url = 'https://www.infoplease.com/search/$1', display = 'Infoplease', tooltip = 'Infoplease encyclopedia, dictionary, and almanac', }, ["jstor"] = { url = 'https://www.jstor.org/action/doBasicSearch?Query=$1&acc=on&wc=on', display = 'JSTOR', description = "[[JSTOR]], an online library containing digitised versions of academic journals. Requires a subscription." }, ["library of congress"] = { url = 'https://www.loc.gov/search/?in=&q=$1', display = "Library of Congress", description = "U.S. [[Library of Congress]]", }, ["mail and guardian"] = { url = 'https://mg.co.za/?s=$1', display = "''Mail & Guardian''", description = "The website of the ''[[Mail & Guardian]]'', a [[newspaper of record]] for South Africa.", }, ["medrs"] = { url = "https://en.wikipedia.org/wiki/Wikipedia:Identifying_reliable_sources_(medicine)", display = "find medical sources", tooltip = "Ideal sources for biomedical material include recent literature reviews and medical guidelines; learn how to identify reliable sources for medical content at Wikipedia at WP:MEDRS.", description = "[[WP:MEDRS|MEDRS]]: Ideal sources for biomedical material include recent literature reviews and medical guidelines; learn how to identify reliable sources for medical content.", }, ["muse"] = { url = 'https://muse.jhu.edu/search?action=search&min=1&max=10&t=header&query=content:$1:and', display = 'MUSE', tooltip = 'Project MUSE: humanities and social science content from academic journals and societies.', }, ["new york times"] = { url = 'https://www.nytimes.com/search/$1', display = "''New York Times''", description = "The website of ''[[The New York Times]]'', a [[newspaper of record]] for the United States.", }, ["new zealand herald"] = { url = 'https://www.nzherald.co.nz/search/$1/', display = "''The New Zealand Herald''", description = "The website of ''[[The New Zealand Herald]]'', a [[newspaper of record]] for New Zealand.", }, ["openlibrary"] = { url = 'https://openlibrary.org/search?q=$1&mode=everything', display = 'OpenLibrary', separator = '+', }, ["openmd"] = { url = "https://openmd.com/search?q=$1", display = "OpenMD", description = "OpenMD: Search engine for medical literature.", }, ["pubmed"] = { url = "https://pubmed.ncbi.nlm.nih.gov/?term=$1&filter=pubt.meta-analysis&filter=pubt.review&filter=pubt.systematicreview&filter=datesearch.y_10", display = "PubMed", description = "[[PubMed]]: Search engine for biomedical literature from [[United States National Library of Medicine|NLM]].", }, ["sciencedirect"] = { url = "https://www.sciencedirect.com/search?qs=$1&articleTypes=REV%2CEN%2CCH%2CSSU%2CPGL&lastSelectedFacet=articleTypes", display = "ScienceDirect", description = "ScienceDirect: [[Elsevier]]'s scientific, technical, and medical research portal.", }, ["south china morning post"] = { url = 'https://www.scmp.com/search/$1', display = "''South China Morning Post''", description = "The website of the ''[[South China Morning Post]]'', a [[newspaper of record]] for Hong Kong.", }, ["springer"] = { url = "https://link.springer.com/search?query=$1", display = "Springer", description = "Springer Nature's portal for journals, books, and reference works.", }, ["statpearls"] = { url = "https://www.ncbi.nlm.nih.gov/books/NBK430685/?term=$1", display = "StatPearls", description = "StatPearls: the largest provider of healthcare continuing education worldwide, providing peer-reviewed practice-guiding knowledge authored by clinical experts.", }, ["straits times"] = { url = 'https://www.straitstimes.com/search?searchkey=$1', display = "''The Straits Times''", description = "The website of ''[[The Straits Times]]'', a [[newspaper of record]] for Singapore.", }, ["sydney morning herald"] = { url = 'https://www.smh.com.au/search?text=$1', display = "''The Sydney Morning Herald''", description = "The website of ''[[The Sydney Morning Herald]]'', a [[newspaper of record]] for Australia.", }, ["the age"] = { url = 'https://www.theage.com.au/search?text=$1', display = "''The Age''", description = "The website of ''[[The Age]]'', a [[newspaper of record]] for Australia.", }, ["the guardian"] = { url = 'https://www.google.co.uk/search?as_sitesearch=www.theguardian.com&q=$1', display = 'The Guardian', description = "''[[The Guardian]]'' newspaper, U.K.", }, ["the hindu"] = { url = 'https://www.thehindu.com/search/?q=$1', display = "''The Hindu''", description = "The website of ''[[The Hindu]]'', a [[newspaper of record]] for India.", }, ["the times"] = { url = 'https://www.thetimes.co.uk/search?source=search-page&q=$1', display = "''The Times''", description = "The website of ''[[The Times]]'', a [[newspaper of record]] for the United Kingdom.", }, ["times of india"] = { url = 'https://timesofindia.indiatimes.com/topic/$1', display = "''The Times of India''", description = "The website of ''[[The Times of India]]'', a [[newspaper of record]] for India.", }, ["trip"] = { url = "https://www.tripdatabase.com/Searchresult?search_type=standard&criteria=$1&from_date=2012", display = "Trip Database", description = "Trip Database: Search engine for clinical research evidence.", }, ["uptodate"] = { url = "https://www.uptodate.com/contents/search?sp=0&searchType=PLAIN_TEXT&source=USER_INPUT&searchControl=TOP_PULLDOWN&searchOffset=1&autoComplete=false&language=en&max=10&search=$1", display = "UpToDate", tooltip = "Evidence-based resource for clinical decision support written for and by physicians", }, ["vgrl"] = { url = 'https://en.wikipedia.org/wiki/Special:Search?search=$1&prefix=Wikipedia%3AWikiProject+Video+games%2FReference+library&fulltext=Search+reference+library&fulltext=Search', display = 'VGRL', description = "[[Wikipedia:WikiProject Video games/Reference library]] internal archive search.", }, ["vgrs"] = { url = 'https://www.google.com/cse?cx=009782238053898643791%3A8naerdbd-oy&q=$1', display = 'VGRS', description = "[http://www.google.com/cse/home?cx=003516479746865699832:leawcwkqifq Google RS], a [[custom Google search engine]] that limits the search to sites listed in [[Wikipedia:WikiProject Video games/Sources]].", }, ["vgtalk"] = { url = 'https://en.wikipedia.org/w/index.php?search=$1+prefix%3AWikipedia+talk%3AWikiProject+Video+games&title=Special:Search&profile=default&fulltext=1', display = 'WPVG Talk', description = "A search in the WikiProject Video games talk page and its archives under [[Wikipedia talk:WikiProject Video games]].", }, ["wikipedia library"] = { url = "https://wikipedialibrary.wmflabs.org/search/?q=$1", display = "TWL", tooltip = "The Wikipedia Library", description = "Search results from dozens of services provided via EBSCOhost to Wikipedians via [[Wikipedia:The Wikipedia Library]].", }, ["wikipedia reference search"] = { url = 'https://www.google.com/custom?hl=en&cx=007734830908295939403%3Agalkqgoksq0&cof=FORID%3A13%3BAH%3Aleft%3BCX%3AWikipedia%2520Reference%2520Search&q=$1', display = 'Wikipedia Reference Search', description = "[[WP:WRS|Wikipedia Reference Search]], a Google search that only searches sites vetted by Wikipedians.", }, ["wiley"] = { url = "https://onlinelibrary.wiley.com/action/doSearch?AllField=$1", display = "Wiley", description = "Wiley Online Library: [[Wiley (publisher)|Wiley's]] portal for academic articles, books, and collections.", }, } 4817c288e68e87936971b4b7bb22926dc62c114c Module:Case templates see also 828 524 1056 2023-07-04T00:39:38Z string2>DB1729 0 Category:Related-topic templates wikitext text/x-wiki * {{tl|R from other capitalisation}} – for categorizing [[WP:Redirect]]s from titles to article (or other pages) where the redirect is just a different capitalization * {{tl|Template capitalization}} – ?? * [[Module:String2]] [[Help:Magic words#Formatting|Magic words]] that rewrite the output (copy-paste will get the text as displayed, not as entered): * <code><nowiki>{{lc:}}</nowiki></code> – lower case output of the full text * <code><nowiki>{{uc:}}</nowiki></code> – upper case output of the full text * <code><nowiki>{{lcfirst:}}</nowiki></code> – lower case output of the first character only * <code><nowiki>{{ucfirst:}}</nowiki></code> – upper case output of the first character only <noinclude> {{Documentation|content= This list is transcluded into the documentation of the various templates it illustrates. [[Category:Related-topic templates]] }}</noinclude> 26b5326d9ef35ba8f23bf11034c0fb1c7ccb1bd4 Module:Citation/CS1/Configuration 828 397 794 2023-07-05T19:02:43Z wikipedia>Trappist the monk 0 bump pmid; Scribunto text/plain local lang_obj = mw.language.getContentLanguage(); -- make a language object for the local language; used here for languages and dates --[[--------------------------< U N C A T E G O R I Z E D _ N A M E S P A C E S >------------------------------ List of namespaces identifiers for namespaces that will not be included in citation error categories. Same as setting notracking = true by default. For wikis that have a current version of Module:cs1 documentation support, this #invoke will return an unordered list of namespace names and their associated identifiers: {{#invoke:cs1 documentation support|uncategorized_namespace_lister|all=<anything>}} ]] uncategorized_namespaces_t = {[2]=true}; -- init with user namespace id for k, _ in pairs (mw.site.talkNamespaces) do -- add all talk namespace ids uncategorized_namespaces_t[k] = true; end local uncategorized_subpages = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'}; -- list of Lua patterns found in page names of pages we should not categorize --[[--------------------------< M E S S A G E S >-------------------------------------------------------------- Translation table The following contains fixed text that may be output as part of a citation. This is separated from the main body to aid in future translations of this module. ]] local messages = { ['agency'] = '$1 $2', -- $1 is sepc, $2 is agency ['archived-dead'] = 'Archived from $1 on $2', ['archived-live'] = '$1 from the original on $2', ['archived-missing'] = 'Archived from the original $1 on $2', ['archived-unfit'] = 'Archived from the original on ', ['archived'] = 'Archived', ['by'] = 'By', -- contributions to authored works: introduction, foreword, afterword ['cartography'] = 'Cartography by $1', ['editor'] = 'ed.', ['editors'] = 'eds.', ['edition'] = '($1&nbsp;ed.)', ['episode'] = 'Episode $1', ['et al'] = 'et&nbsp;al.', ['in'] = 'In', -- edited works ['inactive'] = 'inactive', ['inset'] = '$1 inset', ['interview'] = 'Interviewed by $1', ['lay summary'] = 'Lay summary', ['mismatch'] = '<code class="cs1-code">&#124;$1=</code> / <code class="cs1-code">&#124;$2=</code> mismatch', -- $1 is year param name; $2 is date param name ['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]:&nbsp;$1', ['notitle'] = 'No title', -- for |title=(()) and (in the future) |title=none ['original'] = 'the original', ['origdate'] = ' [$1]', ['published'] = ' (published $1)', ['retrieved'] = 'Retrieved $1', ['season'] = 'Season $1', ['section'] = '§&nbsp;$1', ['sections'] = '§§&nbsp;$1', ['series'] = '$1 $2', -- $1 is sepc, $2 is series ['seriesnum'] = 'Series $1', ['translated'] = 'Translated by $1', ['type'] = ' ($1)', -- for titletype ['written'] = 'Written at $1', ['vol'] = '$1 Vol.&nbsp;$2', -- $1 is sepc; bold journal style volume is in presentation{} ['vol-no'] = '$1 Vol.&nbsp;$2, no.&nbsp;$3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization) ['issue'] = '$1 No.&nbsp;$2', -- $1 is sepc ['art'] = '$1 Art.&nbsp;$2', -- $1 is sepc; for {{cite conference}} only ['vol-art'] = '$1 Vol.&nbsp;$2, art.&nbsp;$3', -- sepc, volume, article-number; for {{cite conference}} only ['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{} ['j-issue'] = ' ($1)', ['j-article-num'] = ' $1', -- TODO: any punctuation here? static text? ['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc ['p-prefix'] = "$1 p.&nbsp;$2", -- $1 is sepc ['pp-prefix'] = "$1 pp.&nbsp;$2", -- $1 is sepc ['j-page(s)'] = ': $1', -- same for page and pages ['sheet'] = '$1 Sheet&nbsp;$2', -- $1 is sepc ['sheets'] = '$1 Sheets&nbsp;$2', -- $1 is sepc ['j-sheet'] = ': Sheet&nbsp;$1', ['j-sheets'] = ': Sheets&nbsp;$1', ['language'] = '(in $1)', ['via'] = " &ndash; via $1", ['event'] = 'Event occurs at', ['minutes'] = 'minutes in', -- Determines the location of the help page ['help page link'] = 'Help:CS1 errors', ['help page label'] = 'help', -- categories ['cat wikilink'] = '[[Category:$1]]', -- $1 is the category name [':cat wikilink'] = '[[:Category:$1|link]]', -- category name as maintenance message wikilink; $1 is the category name -- Internal errors (should only occur if configuration is bad) ['undefined_error'] = 'Called with an undefined error condition', ['unknown_ID_key'] = 'Unrecognized ID key: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{} ['unknown_ID_access'] = 'Unrecognized ID access keyword: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{} ['unknown_argument_map'] = 'Argument map not defined for this variable', ['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty', ['warning_msg_e'] = '<span style="color:#d33">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">&#123;{$1}}</code> templates have errors</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link ['warning_msg_m'] = '<span style="color:#3a3">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">&#123;{$1}}</code> templates have maintenance messages</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link } --[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------ this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the template's documentation when an article is displayed in preview mode. Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here. ]] local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n ['audio-visual'] = 'AV media', ['AV-media-notes'] = 'AV media notes', ['encyclopaedia'] = 'encyclopedia', ['mailinglist'] = 'mailing list', ['pressrelease'] = 'press release' } --[=[-------------------------< E T _ A L _ P A T T E R N S >-------------------------------------------------- This table provides Lua patterns for the phrase "et al" and variants in name text (author, editor, etc.). The main module uses these to identify and emit the 'etal' message. ]=] local et_al_patterns = { "[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme "[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match) "[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al. "%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form "%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax) "[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form } --[[--------------------------< P R E S E N T A T I O N >------------------------ Fixed presentation markup. Originally part of citation_config.messages it has been moved into its own, more semantically correct place. ]] local presentation = { -- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display ['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>', ['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>', ['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>', ['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS ['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like ['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute ['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist ['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc. ['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc ['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored) -- various access levels, for |access=, |doi-access=, |arxiv=, ... -- narrow no-break space &#8239; may work better than nowrap CSS. Or not? Browser support? ['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon ['free'] = {class='cs1-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css ['registration'] = {class='cs1-lock-registration', title='Free registration required'}, ['limited'] = {class='cs1-lock-limited', title='Free access subject to limited trial, subscription normally required'}, ['subscription'] = {class='cs1-lock-subscription', title='Paid subscription required'}, ['interwiki-icon'] = '<span class="$1" title="$2">$3</span>', ['class-wikisource'] = 'cs1-ws-icon', ['italic-title'] = "''$1''", ['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark ['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark ['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span> ['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space) ['ocins'] = '<span title="$1" class="Z3988"></span>', ['parameter'] = '<code class="cs1-code">&#124;$1=</code>', ['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character ['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string) ['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content ['quoted-title'] = '"$1"', ['sep_cs1'] = '.', -- CS1 element separator ['sep_cs2'] = ',', -- CS2 separator ['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon ['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items ['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names ['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space> ['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma ['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space ['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end ['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items ['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items ['trans-italic-title'] = "&#91;''$1''&#93;", ['trans-quoted-title'] = "&#91;$1&#93;", -- for |trans-title= and |trans-quote= ['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{} } --[[--------------------------< A L I A S E S >--------------------------------- Aliases table for commonly passed parameters. Parameter names on the right side in the assignments in this table must have been defined in the Whitelist before they will be recognized as valid parameter names ]] local aliases = { ['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot ['Agency'] = 'agency', ['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot ['ArchiveFormat'] = 'archive-format', ['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot ['ArticleNumber'] = 'article-number', ['ASINTLD'] = 'asin-tld', ['At'] = 'at', -- Used by InternetArchiveBot ['Authors'] = {'authors', 'people', 'credits'}, ['BookTitle'] = {'book-title', 'booktitle'}, ['Cartography'] = 'cartography', ['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'}, ['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format', 'article-format', 'section-format'}; ['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url', 'chapterurl'}, -- Used by InternetArchiveBot ['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access', 'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot ['Class'] = 'class', -- cite arxiv and arxiv identifier ['Collaboration'] = 'collaboration', ['Conference'] = {'conference', 'event'}, ['ConferenceFormat'] = 'conference-format', ['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot ['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only ['Degree'] = 'degree', ['DF'] = 'df', ['DisplayAuthors'] = {'display-authors', 'display-subjects'}, ['DisplayContributors'] = 'display-contributors', ['DisplayEditors'] = 'display-editors', ['DisplayInterviewers'] = 'display-interviewers', ['DisplayTranslators'] = 'display-translators', ['Docket'] = 'docket', ['DoiBroken'] = 'doi-broken-date', ['Edition'] = 'edition', ['Embargo'] = 'pmc-embargo-date', ['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only ['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode? ['Format'] = 'format', ['ID'] = {'id', 'ID'}, ['Inset'] = 'inset', ['Issue'] = {'issue', 'number'}, ['Language'] = {'language', 'lang'}, ['LayDate'] = 'lay-date', ['LayFormat'] = 'lay-format', ['LaySource'] = 'lay-source', ['LayURL'] = 'lay-url', ['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only ['Map'] = 'map', -- cite map only ['MapFormat'] = 'map-format', -- cite map only ['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot ['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot ['Minutes'] = 'minutes', ['Mode'] = 'mode', ['NameListStyle'] = 'name-list-style', ['Network'] = 'network', ['Newsgroup'] = 'newsgroup', -- cite newsgroup only ['NoPP'] = {'no-pp', 'nopp'}, ['NoTracking'] = {'no-tracking', 'template-doc-demo'}, ['Number'] = 'number', -- this case only for cite techreport ['OrigDate'] = {'orig-date', 'orig-year', 'origyear'}, ['Others'] = 'others', ['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot ['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot ['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'}, ['Place'] = {'place', 'location'}, ['PostScript'] = 'postscript', ['PublicationDate'] = {'publication-date', 'publicationdate'}, ['PublicationPlace'] = {'publication-place', 'publicationplace'}, ['PublisherName'] = {'publisher', 'institution'}, ['Quote'] = {'quote', 'quotation'}, ['QuotePage'] = 'quote-page', ['QuotePages'] = 'quote-pages', ['Ref'] = 'ref', ['Scale'] = 'scale', ['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry', 'script-article', 'script-section'}, ['ScriptMap'] = 'script-map', ['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-website', 'script-work'}, ['ScriptQuote'] = 'script-quote', ['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot ['Season'] = 'season', ['Sections'] = 'sections', -- cite map only ['Series'] = {'series', 'version'}, ['SeriesLink'] = {'series-link', 'serieslink'}, ['SeriesNumber'] = {'series-number', 'series-no'}, ['Sheet'] = 'sheet', -- cite map only ['Sheets'] = 'sheets', -- cite map only ['Station'] = 'station', ['Time'] = 'time', ['TimeCaption'] = 'time-caption', ['Title'] = 'title', -- Used by InternetArchiveBot ['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot ['TitleNote'] = 'department', ['TitleType'] = {'type', 'medium'}, ['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution', 'trans-entry', 'trans-section'}, ['Transcript'] = 'transcript', ['TranscriptFormat'] = 'transcript-format', ['TranscriptURL'] = {'transcript-url', 'transcripturl'}, -- Used by InternetArchiveBot ['TransMap'] = 'trans-map', -- cite map only ['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper', 'trans-periodical', 'trans-website', 'trans-work'}, ['TransQuote'] = 'trans-quote', ['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot ['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot ['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot ['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot ['Vauthors'] = 'vauthors', ['Veditors'] = 'veditors', ['Via'] = 'via', ['Volume'] = 'volume', ['Year'] = 'year', ['AuthorList-First'] = {"first#", "author-first#", "author#-first", "given#", "author-given#", "author#-given"}, ['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "surname#", "author-surname#", "author#-surname", "author#", "subject#", 'host#'}, ['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#", "subject#-link", "authorlink#", "author#link"}, ['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"}, ['ContributorList-First'] = {'contributor-first#', 'contributor#-first', 'contributor-given#', 'contributor#-given'}, ['ContributorList-Last'] = {'contributor-last#', 'contributor#-last', 'contributor-surname#', 'contributor#-surname', 'contributor#'}, ['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'}, ['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'}, ['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"}, ['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#", "editor#-surname", "editor#"}, ['EditorList-Link'] = {"editor-link#", "editor#-link"}, ['EditorList-Mask'] = {"editor-mask#", "editor#-mask"}, ['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first', 'interviewer-given#', 'interviewer#-given'}, ['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last', 'interviewer-surname#', 'interviewer#-surname', 'interviewer#'}, ['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'}, ['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'}, ['TranslatorList-First'] = {'translator-first#', 'translator#-first', 'translator-given#', 'translator#-given'}, ['TranslatorList-Last'] = {'translator-last#', 'translator#-last', 'translator-surname#', 'translator#-surname', 'translator#'}, ['TranslatorList-Link'] = {'translator-link#', 'translator#-link'}, ['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'}, } --[[--------------------------< P U N C T _ S K I P >--------------------------- builds a table of parameter names that the extraneous terminal punctuation check should not check. ]] local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value 'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters 'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators 'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous 'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'LayURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters } local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value 'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'LayURL', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls 'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls } local function build_skip_table (skip_t, meta_params) for _, meta_param in ipairs (meta_params) do -- for each meta parameter key local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name if 'string' == type (params) then skip_t[params] = 1; -- just a single parameter else for _, param in ipairs (params) do -- get the parameter name skip_t[param] = 1; -- add the parameter name to the skip table local count; param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters if 0 ~= count then -- if removed skip_t[param] = 1; -- add param name without enumerator marker end end end end return skip_t; end local punct_skip = {}; local url_skip = {}; --[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >---------- this is a list of tlds that are known to have single-letter second-level domain names. This list does not include ccTLDs which are accepted in is_domain_name(). ]] local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'org', 'today'}; --[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------ This table is primarily here to support internationalization. Translations in this table are used, for example, when an error message, category name, etc., is extracted from the English alias key. There may be other cases where this translation table may be useful. ]] local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143'; local special_case_translation = { ['AuthorList'] = 'authors list', -- used to assemble maintenance category names ['ContributorList'] = 'contributors list', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below ['EditorList'] = 'editors list', -- must match the names of the actual categories ['InterviewerList'] = 'interviewers list', -- this group or translations used by name_has_ed_markup() and name_has_mult_names() ['TranslatorList'] = 'translators list', -- Lua patterns to match pseudo-titles used by InternetArchiveBot and others as placeholder for unknown |title= value ['archived_copy'] = { -- used with CS1 maint: Archive[d] copy as title ['en'] = '^archived?%s+copy$', -- for English; translators: keep this because templates imported from en.wiki ['local'] = nil, -- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language }, -- Lua patterns to match generic titles; usually created by bots or reference filling tools -- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language -- generic titles and patterns in this table should be lowercase only -- leave ['local'] nil except when there is a matching generic title in your language -- boolean 'true' for plain-text searches; 'false' for pattern searches ['generic_titles'] = { ['accept'] = { }, ['reject'] = { {['en'] = {'^wayback%s+machine$', false}, ['local'] = nil}, {['en'] = {'are you a robot', true}, ['local'] = nil}, {['en'] = {'hugedomains.com', true}, ['local'] = nil}, {['en'] = {'^[%(%[{<]?no +title[>}%]%)]?$', false}, ['local'] = nil}, {['en'] = {'page not found', true}, ['local'] = nil}, {['en'] = {'subscribe to read', true}, ['local'] = nil}, {['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = nil}, {['en'] = {'website is for sale', true}, ['local'] = nil}, {['en'] = {'^404', false}, ['local'] = nil}, {['en'] = {'internet archive wayback machine', true}, ['local'] = nil}, {['en'] = {'log into facebook', true}, ['local'] = nil}, {['en'] = {'login • instagram', true}, ['local'] = nil}, {['en'] = {'redirecting...', true}, ['local'] = nil}, {['en'] = {'usurped title', true}, ['local'] = nil}, -- added by a GreenC bot {['en'] = {'webcite query result', true}, ['local'] = nil}, {['en'] = {'wikiwix\'s cache', true}, ['local'] = nil}, } }, -- boolean 'true' for plain-text searches, search string must be lowercase only -- boolean 'false' for pattern searches -- leave ['local'] nil except when there is a matching generic name in your language ['generic_names'] = { ['accept'] = { {['en'] = {'%[%[[^|]*%(author%) *|[^%]]*%]%]', false}, ['local'] = nil}, }, ['reject'] = { {['en'] = {'about us', true}, ['local'] = nil}, {['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil}, {['en'] = {'allmusic', true}, ['local'] = nil}, {['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = nil}, {['en'] = {'business', true}, ['local'] = nil}, {['en'] = {'cnn', true}, ['local'] = nil}, {['en'] = {'collaborator', true}, ['local'] = nil}, {['en'] = {'contributor', true}, ['local'] = nil}, {['en'] = {'contact us', true}, ['local'] = nil}, {['en'] = {'directory', true}, ['local'] = nil}, {['en'] = {'%f[%(%[][%(%[]%s*eds?%.?%s*[%)%]]?$', false}, ['local'] = nil}, {['en'] = {'[,%.%s]%f[e]eds?%.?$', false}, ['local'] = nil}, {['en'] = {'^eds?[%.,;]', false}, ['local'] = nil}, {['en'] = {'^[%(%[]%s*[Ee][Dd][Ss]?%.?%s*[%)%]]', false}, ['local'] = nil}, {['en'] = {'%f[%a][Ee]dited%f[%A]', false}, ['local'] = nil}, {['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = nil}, {['en'] = {'%f[%a]]Ee]mail%f[%A]', false}, ['local'] = nil}, {['en'] = {'facebook', true}, ['local'] = nil}, {['en'] = {'google', true}, ['local'] = nil}, {['en'] = {'home page', true}, ['local'] = nil}, {['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil}, {['en'] = {'instagram', true}, ['local'] = nil}, {['en'] = {'interviewer', true}, ['local'] = nil}, {['en'] = {'linkedIn', true}, ['local'] = nil}, {['en'] = {'^[Nn]ews$', false}, ['local'] = nil}, {['en'] = {'pinterest', true}, ['local'] = nil}, {['en'] = {'policy', true}, ['local'] = nil}, {['en'] = {'privacy', true}, ['local'] = nil}, {['en'] = {'reuters', true}, ['local'] = nil}, {['en'] = {'translator', true}, ['local'] = nil}, {['en'] = {'tumblr', true}, ['local'] = nil}, {['en'] = {'twitter', true}, ['local'] = nil}, {['en'] = {'site name', true}, ['local'] = nil}, {['en'] = {'statement', true}, ['local'] = nil}, {['en'] = {'submitted', true}, ['local'] = nil}, {['en'] = {'super.?user', false}, ['local'] = nil}, {['en'] = {'%f['..is_Latn..'][Uu]ser%f[^'..is_Latn..']', false}, ['local'] = nil}, {['en'] = {'verfasser', true}, ['local'] = nil}, } } } --[[--------------------------< D A T E _ N A M E S >---------------------------------------------------------- This table of tables lists local language date names and fallback English date names. The code in Date_validation will look first in the local table for valid date names. If date names are not found in the local table, the code will look in the English table. Because citations can be copied to the local wiki from en.wiki, the English is required when the date-name translation function date_name_xlate() is used. In these tables, season numbering is defined by Extended Date/Time Format (EDTF) Specification (https://www.loc.gov/standards/datetime/) which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using numbers 21-41. CS1|2 only supports generic seasons. EDTF does support the distinction between north and south hemisphere seasons but CS1|2 has no way to make that distinction. 33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each) The standard does not address 'named' dates so, for the purposes of CS1|2, Easter and Christmas are defined here as 98 and 99, which should be out of the ISO 8601 (EDTF) range of uses for a while. local_date_names_from_mediawiki is a boolean. When set to: true – module will fetch local month names from MediaWiki for both date_names['local']['long'] and date_names['local']['short'] false – module will *not* fetch local month names from MediaWiki Caveat lector: There is no guarantee that MediaWiki will provide short month names. At your wiki you can test the results of the MediaWiki fetch in the debug console with this command (the result is alpha sorted): =mw.dumpObject (p.date_names['local']) While the module can fetch month names from MediaWiki, it cannot fetch the quarter, season, and named date names from MediaWiki. Those must be translated manually. ]] local local_date_names_from_mediawiki = true; -- when false, manual translation required for date_names['local']['long'] and date_names['local']['short'] -- when true, module fetches long and short month names from MediaWiki local date_names = { ['en'] = { -- English ['long'] = {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12}, ['short'] = {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12}, ['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36}, ['season'] = {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23}, ['named'] = {['Easter'] = 98, ['Christmas'] = 99}, }, -- when local_date_names_from_mediawiki = false ['local'] = { -- replace these English date names with the local language equivalents ['long'] = {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12}, ['short'] = {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12}, ['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36}, ['season'] = {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23}, ['named'] = {['Easter'] = 98, ['Christmas'] = 99}, }, ['inv_local_long'] = {}, -- used in date reformatting & translation; copy of date_names['local'].long where k/v are inverted: [1]='<local name>' etc. ['inv_local_short'] = {}, -- used in date reformatting & translation; copy of date_names['local'].short where k/v are inverted: [1]='<local name>' etc. ['inv_local_quarter'] = {}, -- used in date translation; copy of date_names['local'].quarter where k/v are inverted: [1]='<local name>' etc. ['inv_local_season'] = {}, -- used in date translation; copy of date_names['local'].season where k/v are inverted: [1]='<local name>' etc. ['inv_local_named'] = {}, -- used in date translation; copy of date_names['local'].named where k/v are inverted: [1]='<local name>' etc. ['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'}, -- used to convert local language digits to Western 0-9 ['xlate_digits'] = {}, } if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled local long_t = {}; local short_t = {}; for i=1, 12 do -- loop 12x and local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i long_t[name] = i; -- save it name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i short_t[name] = i; -- save it end date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation end -- create inverted date-name tables for reformatting and/or translation for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd end end for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value end local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}} '{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count '{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k '{{ *[Uu]se +(MDY) +dates *[|}]', -- 788 '{{ *[Uu]se +(DMY) +dates *[|}]', -- 343 '{{ *([Mm]dy) *[|}]', -- 176 '{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18 '{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11 '{{ *([Dd]my) *[|}]', -- 56 '{{ *[Uu]se +(MDY) *[|}]', -- 5 '{{ *([Dd]MY) *[|}]', -- 3 '{{ *[Uu]se(mdy)dates *[|}]', -- 1 '{{ *[Uu]se +(DMY) *[|}]', -- 0 '{{ *([Mm]DY) *[|}]', -- 0 } local function get_date_format () local title_object = mw.title.getCurrentTitle(); if title_object.namespace == 10 then -- not in template space so that unused templates appear in unused-template-reports; return nil; -- auto-formatting does not work in Template space so don't set global_df end local content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625 for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format if match then content = content:match ('%b{}', start); -- get the whole template if content:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length return match:lower() .. '-' .. content:match ('| *cs1%-dates *= *([lsy][sy]?)'); else return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df= end end end end local global_df; --[[-----------------< V O L U M E , I S S U E , P A G E S >------------------ These tables hold cite class values (from the template invocation) and identify those templates that support |volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which is handled in the main module. ]] local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'} local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'} local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'} --[[ These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1 templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must not support |volume=. ]] local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used 'website', 'mailinglist', 'script-website', } local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used 'journal', 'magazine', 'newspaper', 'periodical', 'work', 'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work', } --[[ Patterns for finding extra text in |volume=, |issue=, |page=, |pages= ]] local vol_iss_pg_patterns = { good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not bad_ppatterns = { -- patterns for |page= and |pages= '^[Pp][PpGg]?%.?[ %d]', '^[Pp][Pp]?%.&nbsp;', -- from {{p.}} and {{pp.}} templates '^[Pp]ages?', '^[Pp]gs.?', }, vpatterns = { -- patterns for |volume= '^volumes?', '^vols?[%.:=]?' }, ipatterns = { -- patterns for |issue= '^issues?', '^iss[%.:=]?', '^numbers?', '^nos?%A', -- don't match 'november' or 'nostradamus' '^nr[%.:=]?', '^n[%.:= ]' -- might be a valid issue without separator (space char is sep char here) } } --[[--------------------------< K E Y W O R D S >------------------------------- These tables hold keywords for those parameters that have defined sets of acceptable keywords. ]] --[[-------------------< K E Y W O R D S T A B L E >-------------------------- this is a list of keywords; each key in the list is associated with a table of synonymous keywords possibly from different languages. for I18N: add local-language keywords to value table; do not change the key. For example, adding the German keyword 'ja': ['affirmative'] = {'yes', 'true', 'y', 'ja'}, Because CS1|2 templates from en.wiki articles are often copied to other local wikis, it is recommended that the English keywords remain in these tables. ]] local keywords = { ['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style= ['and'] = {'and', 'serial'}, -- |name-list-style= ['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot ['afterword'] = {'afterword'}, -- |contribution= ['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot ['cs1'] = {'cs1'}, -- |mode= ['cs2'] = {'cs2'}, -- |mode= ['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot ['dmy'] = {'dmy'}, -- |df= ['dmy-all'] = {'dmy-all'}, -- |df= ['foreword'] = {'foreword'}, -- |contribution= ['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot ['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true ['introduction'] = {'introduction'}, -- |contribution= ['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot ['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot ['mdy'] = {'mdy'}, -- |df= ['mdy-all'] = {'mdy-all'}, -- |df= ['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot ['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=) ['preface'] = {'preface'}, -- |contribution= ['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot ['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot ['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot ['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot ['vanc'] = {'vanc'}, -- |name-list-style= ['ymd'] = {'ymd'}, -- |df= ['ymd-all'] = {'ymd-all'}, -- |df= -- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki -- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki } --[[------------------------< X L A T E _ K E Y W O R D S >--------------------- this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{} become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}: ['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{} becomes ['yes'] = 'affirmative', -- in keywords_xlate{} ['true'] = 'affirmative', ['y'] = 'affirmative', the purpose of this function is to act as a translator between a non-English keyword and its English equivalent that may be used in other modules of this suite ]] local function xlate_keywords () local out_table = {}; -- output goes here for k, keywords_t in pairs (keywords) do -- spin through the keywords table for _, keyword in ipairs (keywords_t) do -- for each keyword out_table[keyword] = k; -- create an entry in the output table where keyword is the key end end return out_table; end local keywords_xlate = xlate_keywords (); -- the list of translated keywords --[[----------------< M A K E _ K E Y W O R D S _ L I S T >--------------------- this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter. keywords_lists{}, is a table of tables from keywords{} ]] local function make_keywords_list (keywords_lists) local out_table = {}; -- output goes here for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ... table.insert (out_table, keyword); -- ... as plain text, to the output list end end return out_table; end --[[----------------< K E Y W O R D S _ L I S T S >----------------------------- this is a list of lists of valid keywords for the various parameters in [key]. Generally the keys in this table are the canonical en.wiki parameter names though some are contrived because of use in multiple differently named parameters: ['yes_true_y'], ['id-access']. The function make_keywords_list() extracts the individual keywords from the appropriate list in keywords{}. The lists in this table are used to validate the keyword assignment for the parameters named in this table's keys. ]] local keywords_lists = { ['yes_true_y'] = make_keywords_list ({keywords.affirmative}), ['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}), ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}), -- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki ['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}), ['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}), ['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported ['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}), ['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}), ['id-access'] = make_keywords_list ({keywords.free}), } --[[---------------------< S T R I P M A R K E R S >---------------------------- Common pattern definition location for stripmarkers so that we don't have to go hunting for them if (when) MediaWiki changes their form. ]] local stripmarkers = { ['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker ['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker() } --[[------------< I N V I S I B L E _ C H A R A C T E R S >--------------------- This table holds non-printing or invisible characters indexed either by name or by Unicode group. Values are decimal representations of UTF-8 codes. The table is organized as a table of tables because the Lua pairs keyword returns table data in an arbitrary order. Here, we want to process the table from top to bottom because the entries at the top of the table are also found in the ranges specified by the entries at the bottom of the table. Also here is a pattern that recognizes stripmarkers that begin and end with the delete characters. The nowiki stripmarker is not an error but some others are because the parameter values that include them become part of the template's metadata before stripmarker replacement. ]] local invisible_defs = { del = '\127', -- used to distinguish between stripmarker and del char zwj = '\226\128\141', -- used with capture because zwj may be allowed } local invisible_chars = { {'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD {'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed {'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B {'hair space', '\226\128\138'}, -- U+200A, E2 80 8A {'soft hyphen', '\194\173'}, -- U+00AD, C2 AD {'horizontal tab', '\009'}, -- U+0009 (HT), 09 {'line feed', '\010'}, -- U+000A (LF), 0A {'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0 {'carriage return', '\013'}, -- U+000D (CR), 0D {'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type {'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker {'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D)) {'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F -- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF -- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF -- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD -- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD } --[[ Indic script makes use of zero width joiner as a character modifier so zwj characters must be left in. This pattern covers all of the unicode characters for these languages: Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf plus the not-necessarily Indic scripts for Sinhala and Burmese: Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf the pattern is used by has_invisible_chars() and coins_cleanup() ]] local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]'; -- list of emoji that use a zwj character (U+200D) to combine with another emoji -- from: https://unicode.org/Public/emoji/15.0/emoji-zwj-sequences.txt; version: 15.0; 2022-05-06 -- table created by: [[:en:Module:Make emoji zwj table]] local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx [9760] = true, -- U+2620 ☠ skull and crossbones [9792] = true, -- U+2640 ♀ female sign [9794] = true, -- U+2642 ♂ male sign [9877] = true, -- U+2695 ⚕ staff of aesculapius [9878] = true, -- U+2696 ⚖ scales [9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign [9992] = true, -- U+2708 ✈ airplane [10052] = true, -- U+2744 ❄ snowflake [10084] = true, -- U+2764 ❤ heavy black heart [11035] = true, -- U+2B1B ⬛ black large square [127752] = true, -- U+1F308 🌈 rainbow [127787] = true, -- U+1F32B 🌫 fog [127806] = true, -- U+1F33E 🌾 ear of rice [127859] = true, -- U+1F373 🍳 cooking [127868] = true, -- U+1F37C 🍼 baby bottle [127876] = true, -- U+1F384 🎄 christmas tree [127891] = true, -- U+1F393 🎓 graduation cap [127908] = true, -- U+1F3A4 🎤 microphone [127912] = true, -- U+1F3A8 🎨 artist palette [127979] = true, -- U+1F3EB 🏫 school [127981] = true, -- U+1F3ED 🏭 factory [128102] = true, -- U+1F466 👦 boy [128103] = true, -- U+1F467 👧 girl [128104] = true, -- U+1F468 👨 man [128105] = true, -- U+1F469 👩 woman [128139] = true, -- U+1F48B 💋 kiss mark [128168] = true, -- U+1F4A8 💨 dash symbol [128171] = true, -- U+1F4AB 💫 dizzy symbol [128187] = true, -- U+1F4BB 💻 personal computer [128188] = true, -- U+1F4BC 💼 brief case [128293] = true, -- U+1F525 🔥 fire [128295] = true, -- U+1F527 🔧 wrench [128300] = true, -- U+1F52C 🔬 microscope [128488] = true, -- U+1F5E8 🗨 left speech bubble [128640] = true, -- U+1F680 🚀 rocket [128658] = true, -- U+1F692 🚒 fire engine [129309] = true, -- U+1F91D 🤝 handshake [129455] = true, -- U+1F9AF 🦯 probing cane [129456] = true, -- U+1F9B0 🦰 emoji component red hair [129457] = true, -- U+1F9B1 🦱 emoji component curly hair [129458] = true, -- U+1F9B2 🦲 emoji component bald [129459] = true, -- U+1F9B3 🦳 emoji component white hair [129466] = true, -- U+1F9BA 🦺 safety vest [129468] = true, -- U+1F9BC 🦼 motorized wheelchair [129469] = true, -- U+1F9BD 🦽 manual wheelchair [129489] = true, -- U+1F9D1 🧑 adult [129657] = true, -- U+1FA79 🩹 adhesive bandage [129778] = true, -- U+1FAF2 🫲 leftwards hand } --[[----------------------< L A N G U A G E S U P P O R T >------------------- These tables and constants support various language-specific functionality. ]] --local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code if string.match (mw.site.server, 'wikidata') then this_wiki_code = mw.getCurrentFrame():preprocess('{{int:lang}}'); -- on Wikidata so use interface language setting instead end local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests local mw_languages_by_name_t = {}; for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language= v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>] if mw_languages_by_name_t[v] then -- when name already in the table if 2 == #k or 3 == #k then -- if tag does not have subtags mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name end else -- here when name not in the table mw_languages_by_name_t[v] = k; -- so add name and matching tag end end local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local) if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag inter_wiki_map[v["prefix"]] = true; -- add it to our local map end end --[[--------------------< S C R I P T _ L A N G _ C O D E S >------------------- This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character language codes that apply only to |script-title= and |script-chapter= ]] local script_lang_codes = { 'ab', 'am', 'ar', 'be', 'bg', 'bn', 'bo', 'bs', 'dv', 'dz', 'el', 'fa', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko', 'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mr', 'my', 'ne', 'or', 'ota', 'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt', 'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh' }; --[[---------------< L A N G U A G E R E M A P P I N G >---------------------- These tables hold language information that is different (correct) from MediaWiki's definitions For each ['code'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', 'code'} in lang_name_remap{} lang_code_remap{}: key is always lowercase ISO 639-1, -2, -3 language code or a valid lowercase IETF language tag value is properly spelled and capitalized language name associated with key only one language name per key; key/value pair must have matching entry in lang_name_remap{} lang_name_remap{}: key is always lowercase language name value is a table the holds correctly spelled and capitalized language name [1] and associated code [2] (code must match a code key in lang_code_remap{}) may have multiple keys referring to a common preferred name and code; For example: ['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh' ]] local lang_code_remap = { -- used for |language= and |script-title= / |script-chapter= ['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch ['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org ['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name ['bn'] = 'Bengali', -- MediaWiki returns Bangla ['ca-valencia'] = 'Valencian', -- IETF variant of Catalan ['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name ['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name ['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data ['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data ['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name } local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase ['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org ['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap ['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code ['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org ['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found ['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name ['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh ['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name ['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö) ['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name ['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639 ['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese ['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found ['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian } --[[---------------< P R O P E R T I E S _ C A T E G O R I E S >---------------- Properties categories. These are used for investigating qualities of citations. ]] local prop_cats = { ['foreign-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code ['foreign-lang-source-2'] = 'CS1 foreign language sources (ISO 639-2)|$1', -- |language= category; a cat for ISO639-2 languages; $1 is the ISO 639-2 code used as a sort key ['jul-greg-uncertainty'] = 'CS1: Julian–Gregorian uncertainty', -- probably temporary cat to identify scope of template with dates 1 October 1582 – 1 January 1926 ['local-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is local-language name, $2 is ISO639-1 code; not emitted when local_lang_cat_enable is false ['location-test'] = 'CS1 location test', ['long-vol'] = 'CS1: long volume value', -- probably temporary cat to identify scope of |volume= values longer than 4 characters ['script'] = 'CS1 uses $1-language script ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is ISO639-1 code ['tracked-param'] = 'CS1 tracked parameter: $1', -- $1 is base (enumerators removed) parameter name ['year-range-abbreviated'] = 'CS1: abbreviated year range', -- probably temporary cat to identify scope of |date=, |year= values using YYYY–YY form } --[[-------------------< T I T L E _ T Y P E S >-------------------------------- Here we map a template's CitationClass to TitleType (default values for |type= parameter) ]] local title_types = { ['AV-media-notes'] = 'Media notes', ['interview'] = 'Interview', ['mailinglist'] = 'Mailing list', ['map'] = 'Map', ['podcast'] = 'Podcast', ['pressrelease'] = 'Press release', ['report'] = 'Report', ['speech'] = 'Speech', ['techreport'] = 'Technical report', ['thesis'] = 'Thesis', } --[[===================<< E R R O R M E S S A G I N G >>====================== ]] --[[----------< E R R O R M E S S A G E S U P P L I M E N T S >------------- I18N for those messages that are supplemented with additional specific text that describes the reason for the error TODO: merge this with special_case_translations{}? ]] local err_msg_supl = { ['char'] = 'invalid character', -- |isbn=, |sbn= ['check'] = 'checksum', -- |isbn=, |sbn= ['flag'] = 'flag', -- |archive-url= ['form'] = 'invalid form', -- |isbn=, |sbn= ['group'] = 'invalid group id', -- |isbn= ['initials'] = 'initials', -- Vancouver ['invalid language code'] = 'invalid language code', -- |script-<param>= ['journal'] = 'journal', -- |bibcode= ['length'] = 'length', -- |isbn=, |bibcode=, |sbn= ['liveweb'] = 'liveweb', -- |archive-url= ['missing comma'] = 'missing comma', -- Vancouver ['missing prefix'] = 'missing prefix', -- |script-<param>= ['missing title part'] = 'missing title part', -- |script-<param>= ['name'] = 'name', -- Vancouver ['non-Latin char'] = 'non-Latin character', -- Vancouver ['path'] = 'path', -- |archive-url= ['prefix'] = 'invalid prefix', -- |isbn= ['punctuation'] = 'punctuation', -- Vancouver ['save'] = 'save command', -- |archive-url= ['suffix'] = 'suffix', -- Vancouver ['timestamp'] = 'timestamp', -- |archive-url= ['unknown language code'] = 'unknown language code', -- |script-<param>= ['value'] = 'value', -- |bibcode= ['year'] = 'year', -- |bibcode= } --[[--------------< E R R O R _ C O N D I T I O N S >--------------------------- Error condition table. This table has two sections: errors at the top, maintenance at the bottom. Maint 'messaging' does not have a 'message' (message=nil) The following contains a list of IDs for various error conditions defined in the code. For each ID, we specify a text message to display, an error category to include, and whether the error message should be wrapped as a hidden comment. Anchor changes require identical changes to matching anchor in Help:CS1 errors TODO: rename error_conditions{} to something more generic; create separate error and maint tables inside that? ]] local error_conditions = { err_accessdate_missing_url = { message = '<code class="cs1-code">&#124;access-date=</code> requires <code class="cs1-code">&#124;url=</code>', anchor = 'accessdate_missing_url', category = 'CS1 errors: access-date without URL', hidden = false }, err_apostrophe_markup = { message = 'Italic or bold markup not allowed in: <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'apostrophe_markup', category = 'CS1 errors: markup', hidden = false }, err_archive_missing_date = { message = '<code class="cs1-code">&#124;archive-url=</code> requires <code class="cs1-code">&#124;archive-date=</code>', anchor = 'archive_missing_date', category = 'CS1 errors: archive-url', hidden = false }, err_archive_missing_url = { message = '<code class="cs1-code">&#124;archive-url=</code> requires <code class="cs1-code">&#124;url=</code>', anchor = 'archive_missing_url', category = 'CS1 errors: archive-url', hidden = false }, err_archive_url = { message = '<code class="cs1-code">&#124;archive-url=</code> is malformed: $1', -- $1 is error message detail anchor = 'archive_url', category = 'CS1 errors: archive-url', hidden = false }, err_arxiv_missing = { message = '<code class="cs1-code">&#124;arxiv=</code> required', anchor = 'arxiv_missing', category = 'CS1 errors: arXiv', -- same as bad arxiv hidden = false }, err_asintld_missing_asin = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;asin=</code>', -- $1 is parameter name anchor = 'asintld_missing_asin', category = 'CS1 errors: ASIN TLD', hidden = false }, err_bad_arxiv = { message = 'Check <code class="cs1-code">&#124;arxiv=</code> value', anchor = 'bad_arxiv', category = 'CS1 errors: arXiv', hidden = false }, err_bad_asin = { message = 'Check <code class="cs1-code">&#124;asin=</code> value', anchor = 'bad_asin', category ='CS1 errors: ASIN', hidden = false }, err_bad_asin_tld = { message = 'Check <code class="cs1-code">&#124;asin-tld=</code> value', anchor = 'bad_asin_tld', category ='CS1 errors: ASIN TLD', hidden = false }, err_bad_bibcode = { message = 'Check <code class="cs1-code">&#124;bibcode=</code> $1', -- $1 is error message detail anchor = 'bad_bibcode', category = 'CS1 errors: bibcode', hidden = false }, err_bad_biorxiv = { message = 'Check <code class="cs1-code">&#124;biorxiv=</code> value', anchor = 'bad_biorxiv', category = 'CS1 errors: bioRxiv', hidden = false }, err_bad_citeseerx = { message = 'Check <code class="cs1-code">&#124;citeseerx=</code> value', anchor = 'bad_citeseerx', category = 'CS1 errors: citeseerx', hidden = false }, err_bad_date = { message = 'Check date values in: $1', -- $1 is a parameter name list anchor = 'bad_date', category = 'CS1 errors: dates', hidden = false }, err_bad_doi = { message = 'Check <code class="cs1-code">&#124;doi=</code> value', anchor = 'bad_doi', category = 'CS1 errors: DOI', hidden = false }, err_bad_hdl = { message = 'Check <code class="cs1-code">&#124;hdl=</code> value', anchor = 'bad_hdl', category = 'CS1 errors: HDL', hidden = false }, err_bad_isbn = { message = 'Check <code class="cs1-code">&#124;isbn=</code> value: $1', -- $1 is error message detail anchor = 'bad_isbn', category = 'CS1 errors: ISBN', hidden = false }, err_bad_ismn = { message = 'Check <code class="cs1-code">&#124;ismn=</code> value', anchor = 'bad_ismn', category = 'CS1 errors: ISMN', hidden = false }, err_bad_issn = { message = 'Check <code class="cs1-code">&#124;$1issn=</code> value', -- $1 is 'e' or '' for eissn or issn anchor = 'bad_issn', category = 'CS1 errors: ISSN', hidden = false }, err_bad_jfm = { message = 'Check <code class="cs1-code">&#124;jfm=</code> value', anchor = 'bad_jfm', category = 'CS1 errors: JFM', hidden = false }, err_bad_jstor = { message = 'Check <code class="cs1-code">&#124;jstor=</code> value', anchor = 'bad_jstor', category = 'CS1 errors: JSTOR', hidden = false }, err_bad_lccn = { message = 'Check <code class="cs1-code">&#124;lccn=</code> value', anchor = 'bad_lccn', category = 'CS1 errors: LCCN', hidden = false }, err_bad_mr = { message = 'Check <code class="cs1-code">&#124;mr=</code> value', anchor = 'bad_mr', category = 'CS1 errors: MR', hidden = false }, err_bad_oclc = { message = 'Check <code class="cs1-code">&#124;oclc=</code> value', anchor = 'bad_oclc', category = 'CS1 errors: OCLC', hidden = false }, err_bad_ol = { message = 'Check <code class="cs1-code">&#124;ol=</code> value', anchor = 'bad_ol', category = 'CS1 errors: OL', hidden = false }, err_bad_osti = { message = 'Check <code class="cs1-code">&#124;osti=</code> value', anchor = 'bad_osti', category = 'CS1 errors: OSTI', hidden = false }, err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link= message = 'Check <code class="cs1-code">&#124;$1=</code> value', -- $1 is parameter name anchor = 'bad_paramlink', category = 'CS1 errors: parameter link', hidden = false }, err_bad_pmc = { message = 'Check <code class="cs1-code">&#124;pmc=</code> value', anchor = 'bad_pmc', category = 'CS1 errors: PMC', hidden = false }, err_bad_pmid = { message = 'Check <code class="cs1-code">&#124;pmid=</code> value', anchor = 'bad_pmid', category = 'CS1 errors: PMID', hidden = false }, err_bad_rfc = { message = 'Check <code class="cs1-code">&#124;rfc=</code> value', anchor = 'bad_rfc', category = 'CS1 errors: RFC', hidden = false }, err_bad_s2cid = { message = 'Check <code class="cs1-code">&#124;s2cid=</code> value', anchor = 'bad_s2cid', category = 'CS1 errors: S2CID', hidden = false }, err_bad_sbn = { message = 'Check <code class="cs1-code">&#124;sbn=</code> value: $1', -- $1 is error message detail anchor = 'bad_sbn', category = 'CS1 errors: SBN', hidden = false }, err_bad_ssrn = { message = 'Check <code class="cs1-code">&#124;ssrn=</code> value', anchor = 'bad_ssrn', category = 'CS1 errors: SSRN', hidden = false }, err_bad_url = { message = 'Check $1 value', -- $1 is parameter name anchor = 'bad_url', category = 'CS1 errors: URL', hidden = false }, err_bad_usenet_id = { message = 'Check <code class="cs1-code">&#124;message-id=</code> value', anchor = 'bad_message_id', category = 'CS1 errors: message-id', hidden = false }, err_bad_zbl = { message = 'Check <code class="cs1-code">&#124;zbl=</code> value', anchor = 'bad_zbl', category = 'CS1 errors: Zbl', hidden = false }, err_bare_url_missing_title = { message = '$1 missing title', -- $1 is parameter name anchor = 'bare_url_missing_title', category = 'CS1 errors: bare URL', hidden = false }, err_biorxiv_missing = { message = '<code class="cs1-code">&#124;biorxiv=</code> required', anchor = 'biorxiv_missing', category = 'CS1 errors: bioRxiv', -- same as bad bioRxiv hidden = false }, err_chapter_ignored = { message = '<code class="cs1-code">&#124;$1=</code> ignored', -- $1 is parameter name anchor = 'chapter_ignored', category = 'CS1 errors: chapter ignored', hidden = false }, err_citation_missing_title = { message = 'Missing or empty <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'citation_missing_title', category = 'CS1 errors: missing title', hidden = false }, err_citeseerx_missing = { message = '<code class="cs1-code">&#124;citeseerx=</code> required', anchor = 'citeseerx_missing', category = 'CS1 errors: citeseerx', -- same as bad citeseerx hidden = false }, err_cite_web_url = { -- this error applies to cite web and to cite podcast message = 'Missing or empty <code class="cs1-code">&#124;url=</code>', anchor = 'cite_web_url', category = 'CS1 errors: requires URL', hidden = false }, err_class_ignored = { message = '<code class="cs1-code">&#124;class=</code> ignored', anchor = 'class_ignored', category = 'CS1 errors: class', hidden = false }, err_contributor_ignored = { message = '<code class="cs1-code">&#124;contributor=</code> ignored', anchor = 'contributor_ignored', category = 'CS1 errors: contributor', hidden = false }, err_contributor_missing_required_param = { message = '<code class="cs1-code">&#124;contributor=</code> requires <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'contributor_missing_required_param', category = 'CS1 errors: contributor', hidden = false }, err_deprecated_params = { message = 'Cite uses deprecated parameter <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'deprecated_params', category = 'CS1 errors: deprecated parameters', hidden = false }, err_disp_name = { message = 'Invalid <code class="cs1-code">&#124;$1=$2</code>', -- $1 is parameter name; $2 is the assigned value anchor = 'disp_name', category = 'CS1 errors: display-names', hidden = false, }, err_doibroken_missing_doi = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;doi=</code>', -- $1 is parameter name anchor = 'doibroken_missing_doi', category = 'CS1 errors: DOI', hidden = false }, err_embargo_missing_pmc = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;pmc=</code>', -- $1 is parameter name anchor = 'embargo_missing_pmc', category = 'CS1 errors: PMC embargo', hidden = false }, err_empty_citation = { message = 'Empty citation', anchor = 'empty_citation', category = 'CS1 errors: empty citation', hidden = false }, err_etal = { message = 'Explicit use of et al. in: <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'explicit_et_al', category = 'CS1 errors: explicit use of et al.', hidden = false }, err_extra_text_edition = { message = '<code class="cs1-code">&#124;edition=</code> has extra text', anchor = 'extra_text_edition', category = 'CS1 errors: extra text: edition', hidden = false, }, err_extra_text_issue = { message = '<code class="cs1-code">&#124;$1=</code> has extra text', -- $1 is parameter name anchor = 'extra_text_issue', category = 'CS1 errors: extra text: issue', hidden = false, }, err_extra_text_pages = { message = '<code class="cs1-code">&#124;$1=</code> has extra text', -- $1 is parameter name anchor = 'extra_text_pages', category = 'CS1 errors: extra text: pages', hidden = false, }, err_extra_text_volume = { message = '<code class="cs1-code">&#124;$1=</code> has extra text', -- $1 is parameter name anchor = 'extra_text_volume', category = 'CS1 errors: extra text: volume', hidden = true, }, err_first_missing_last = { message = '<code class="cs1-code">&#124;$1=</code> missing <code class="cs1-code">&#124;$2=</code>', -- $1 is first alias, $2 is matching last alias anchor = 'first_missing_last', category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator hidden = false }, err_format_missing_url = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;$2=</code>', -- $1 is format parameter $2 is url parameter anchor = 'format_missing_url', category = 'CS1 errors: format without URL', hidden = false }, err_generic_name = { message = '<code class="cs1-code">&#124;$1=</code> has generic name', -- $1 is parameter name anchor = 'generic_name', category = 'CS1 errors: generic name', hidden = false, }, err_generic_title = { message = 'Cite uses generic title', anchor = 'generic_title', category = 'CS1 errors: generic title', hidden = false, }, err_invalid_param_val = { message = 'Invalid <code class="cs1-code">&#124;$1=$2</code>', -- $1 is parameter name $2 is parameter value anchor = 'invalid_param_val', category = 'CS1 errors: invalid parameter value', hidden = false }, err_invisible_char = { message = '$1 in $2 at position $3', -- $1 is invisible char $2 is parameter name $3 is position number anchor = 'invisible_char', category = 'CS1 errors: invisible characters', hidden = false }, err_missing_name = { message = 'Missing <code class="cs1-code">&#124;$1$2=</code>', -- $1 is modified NameList; $2 is enumerator anchor = 'missing_name', category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator hidden = false }, err_missing_periodical = { message = 'Cite $1 requires <code class="cs1-code">&#124;$2=</code>', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1 anchor = 'missing_periodical', category = 'CS1 errors: missing periodical', hidden = true }, err_missing_pipe = { message = 'Missing pipe in: <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'missing_pipe', category = 'CS1 errors: missing pipe', hidden = false }, err_param_access_requires_param = { message = '<code class="cs1-code">&#124;$1-access=</code> requires <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'param_access_requires_param', category = 'CS1 errors: param-access', hidden = false }, err_param_has_ext_link = { message = 'External link in <code class="cs1-code">$1</code>', -- $1 is parameter name anchor = 'param_has_ext_link', category = 'CS1 errors: external links', hidden = false }, err_parameter_ignored = { message = 'Unknown parameter <code class="cs1-code">&#124;$1=</code> ignored', -- $1 is parameter name anchor = 'parameter_ignored', category = 'CS1 errors: unsupported parameter', hidden = false }, err_parameter_ignored_suggest = { message = 'Unknown parameter <code class="cs1-code">&#124;$1=</code> ignored (<code class="cs1-code">&#124;$2=</code> suggested)', -- $1 is unknown parameter $2 is suggested parameter name anchor = 'parameter_ignored_suggest', category = 'CS1 errors: unsupported parameter', hidden = false }, err_redundant_parameters = { message = 'More than one of $1 specified', -- $1 is error message detail anchor = 'redundant_parameters', category = 'CS1 errors: redundant parameter', hidden = false }, err_script_parameter = { message = 'Invalid <code class="cs1-code">&#124;$1=</code>: $2', -- $1 is parameter name $2 is script language code or error detail anchor = 'script_parameter', category = 'CS1 errors: script parameters', hidden = false }, err_ssrn_missing = { message = '<code class="cs1-code">&#124;ssrn=</code> required', anchor = 'ssrn_missing', category = 'CS1 errors: SSRN', -- same as bad arxiv hidden = false }, err_text_ignored = { message = 'Text "$1" ignored', -- $1 is ignored text anchor = 'text_ignored', category = 'CS1 errors: unrecognized parameter', hidden = false }, err_trans_missing_title = { message = '<code class="cs1-code">&#124;trans-$1=</code> requires <code class="cs1-code">&#124;$1=</code> or <code class="cs1-code">&#124;script-$1=</code>', -- $1 is base parameter name anchor = 'trans_missing_title', category = 'CS1 errors: translated title', hidden = false }, err_param_unknown_empty = { message = 'Cite has empty unknown parameter$1: $2', -- $1 is 's' or empty space; $2 is empty unknown param list anchor = 'param_unknown_empty', category = 'CS1 errors: empty unknown parameters', hidden = false }, err_vancouver = { message = 'Vancouver style error: $1 in name $2', -- $1 is error detail, $2 is the nth name anchor = 'vancouver', category = 'CS1 errors: Vancouver style', hidden = false }, err_wikilink_in_url = { message = 'URL–wikilink conflict', -- uses ndash anchor = 'wikilink_in_url', category = 'CS1 errors: URL–wikilink conflict', -- uses ndash hidden = false }, --[[--------------------------< M A I N T >------------------------------------- maint messages do not have a message (message = nil); otherwise the structure is the same as error messages ]] maint_archived_copy = { message = nil, anchor = 'archived_copy', category = 'CS1 maint: archived copy as title', hidden = true, }, maint_authors = { message = nil, anchor = 'authors', category = 'CS1 maint: uses authors parameter', hidden = true, }, maint_bot_unknown = { message = nil, anchor = 'bot:_unknown', category = 'CS1 maint: bot: original URL status unknown', hidden = true, }, maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki message = nil, anchor = 'date_auto_xlated', category = 'CS1 maint: date auto-translated', hidden = true, }, maint_date_format = { message = nil, anchor = 'date_format', category = 'CS1 maint: date format', hidden = true, }, maint_date_year = { message = nil, anchor = 'date_year', category = 'CS1 maint: date and year', hidden = true, }, maint_doi_ignore = { message = nil, anchor = 'doi_ignore', category = 'CS1 maint: ignored DOI errors', hidden = true, }, maint_doi_inactive = { message = nil, anchor = 'doi_inactive', category = 'CS1 maint: DOI inactive', hidden = true, }, maint_doi_inactive_dated = { message = nil, anchor = 'doi_inactive_dated', category = 'CS1 maint: DOI inactive as of $2$3$1', -- $1 is year, $2 is month-name or empty string, $3 is space or empty string hidden = true, }, maint_extra_punct = { message = nil, anchor = 'extra_punct', category = 'CS1 maint: extra punctuation', hidden = true, }, maint_isbn_ignore = { message = nil, anchor = 'ignore_isbn_err', category = 'CS1 maint: ignored ISBN errors', hidden = true, }, maint_issn_ignore = { message = nil, anchor = 'ignore_issn', category = 'CS1 maint: ignored ISSN errors', hidden = true, }, maint_jfm_format = { message = nil, anchor = 'jfm_format', category = 'CS1 maint: JFM format', hidden = true, }, maint_location = { message = nil, anchor = 'location', category = 'CS1 maint: location', hidden = true, }, maint_mr_format = { message = nil, anchor = 'mr_format', category = 'CS1 maint: MR format', hidden = true, }, maint_mult_names = { message = nil, anchor = 'mult_names', category = 'CS1 maint: multiple names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table hidden = true, }, maint_numeric_names = { message = nil, anchor = 'numeric_names', category = 'CS1 maint: numeric names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table hidden = true, }, maint_others = { message = nil, anchor = 'others', category = 'CS1 maint: others', hidden = true, }, maint_others_avm = { message = nil, anchor = 'others_avm', category = 'CS1 maint: others in cite AV media (notes)', hidden = true, }, maint_pmc_embargo = { message = nil, anchor = 'embargo', category = 'CS1 maint: PMC embargo expired', hidden = true, }, maint_pmc_format = { message = nil, anchor = 'pmc_format', category = 'CS1 maint: PMC format', hidden = true, }, maint_postscript = { message = nil, anchor = 'postscript', category = 'CS1 maint: postscript', hidden = true, }, maint_ref_duplicates_default = { message = nil, anchor = 'ref_default', category = 'CS1 maint: ref duplicates default', hidden = true, }, maint_unfit = { message = nil, anchor = 'unfit', category = 'CS1 maint: unfit URL', hidden = true, }, maint_unknown_lang = { message = nil, anchor = 'unknown_lang', category = 'CS1 maint: unrecognized language', hidden = true, }, maint_untitled = { message = nil, anchor = 'untitled', category = 'CS1 maint: untitled periodical', hidden = true, }, maint_url_status = { message = nil, anchor = 'url_status', category = 'CS1 maint: url-status', hidden = true, }, maint_zbl = { message = nil, anchor = 'zbl', category = 'CS1 maint: Zbl', hidden = true, }, } --[[--------------------------< I D _ H A N D L E R S >-------------------------------------------------------- The following contains a list of values for various defined identifiers. For each identifier we specify a variety of information necessary to properly render the identifier in the citation. parameters: a list of parameter aliases for this identifier; first in the list is the canonical form link: Wikipedia article name redirect: a local redirect to a local Wikipedia article name; at en.wiki, 'ISBN (identifier)' is a redirect to 'International Standard Book Number' q: Wikidata q number for the identifier label: the label preceding the identifier; label is linked to a Wikipedia article (in this order): redirect from id_handlers['<id>'].redirect when use_identifier_redirects is true Wikidata-supplied article name for the local wiki from id_handlers['<id>'].q local article name from id_handlers['<id>'].link prefix: the first part of a URL that will be concatenated with a second part which usually contains the identifier suffix: optional third part to be added after the identifier encode: true if URI should be percent-encoded; otherwise false COinS: identifier link or keyword for use in COinS: for identifiers registered at info-uri.info use: info:.... where '...' is the appropriate identifier label for identifiers that have COinS keywords, use the keyword: rft.isbn, rft.issn, rft.eissn for |asin= and |ol=, which require assembly, use the keyword: url for others make a URL using the value in prefix/suffix and #label, use the keyword: pre (not checked; any text other than 'info', 'rft', or 'url' works here) set to nil to leave the identifier out of the COinS separator: character or text between label and the identifier in the rendered citation id_limit: for those identifiers with established limits, this property holds the upper limit access: use this parameter to set the access level for all instances of this identifier. the value must be a valid access level for an identifier (see ['id-access'] in this file). custom_access: to enable custom access level for an identifier, set this parameter to the parameter that should control it (normally 'id-access') ]] local id_handlers = { ['ARXIV'] = { parameters = {'arxiv', 'eprint'}, link = 'arXiv', redirect = 'arXiv (identifier)', q = 'Q118398', label = 'arXiv', prefix = 'https://arxiv.org/abs/', -- protocol-relative tested 2013-09-04 encode = false, COinS = 'info:arxiv', separator = ':', access = 'free', -- free to read }, ['ASIN'] = { parameters = { 'asin', 'ASIN' }, link = 'Amazon Standard Identification Number', redirect = 'ASIN (identifier)', q = 'Q1753278', label = 'ASIN', prefix = 'https://www.amazon.', COinS = 'url', separator = '&nbsp;', encode = false; }, ['BIBCODE'] = { parameters = {'bibcode'}, link = 'Bibcode', redirect = 'Bibcode (identifier)', q = 'Q25754', label = 'Bibcode', prefix = 'https://ui.adsabs.harvard.edu/abs/', encode = false, COinS = 'info:bibcode', separator = ':', custom_access = 'bibcode-access', }, ['BIORXIV'] = { parameters = {'biorxiv'}, link = 'bioRxiv', redirect = 'bioRxiv (identifier)', q = 'Q19835482', label = 'bioRxiv', prefix = 'https://doi.org/', COinS = 'pre', -- use prefix value access = 'free', -- free to read encode = true, separator = '&nbsp;', }, ['CITESEERX'] = { parameters = {'citeseerx'}, link = 'CiteSeerX', redirect = 'CiteSeerX (identifier)', q = 'Q2715061', label = 'CiteSeerX', prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=', COinS = 'pre', -- use prefix value access = 'free', -- free to read encode = true, separator = '&nbsp;', }, ['DOI'] = { -- Used by InternetArchiveBot parameters = { 'doi', 'DOI'}, link = 'Digital object identifier', redirect = 'doi (identifier)', q = 'Q25670', label = 'doi', prefix = 'https://doi.org/', COinS = 'info:doi', separator = ':', encode = true, custom_access = 'doi-access', }, ['EISSN'] = { parameters = {'eissn', 'EISSN'}, link = 'International Standard Serial Number#Electronic ISSN', redirect = 'eISSN (identifier)', q = 'Q46339674', label = 'eISSN', prefix = 'https://www.worldcat.org/issn/', COinS = 'rft.eissn', encode = false, separator = '&nbsp;', }, ['HDL'] = { parameters = { 'hdl', 'HDL' }, link = 'Handle System', redirect = 'hdl (identifier)', q = 'Q3126718', label = 'hdl', prefix = 'https://hdl.handle.net/', COinS = 'info:hdl', separator = ':', encode = true, custom_access = 'hdl-access', }, ['ISBN'] = { -- Used by InternetArchiveBot parameters = {'isbn', 'ISBN'}, link = 'International Standard Book Number', redirect = 'ISBN (identifier)', q = 'Q33057', label = 'ISBN', prefix = 'Special:BookSources/', COinS = 'rft.isbn', separator = '&nbsp;', }, ['ISMN'] = { parameters = {'ismn', 'ISMN'}, link = 'International Standard Music Number', redirect = 'ISMN (identifier)', q = 'Q1666938', label = 'ISMN', prefix = '', -- not currently used; COinS = nil, -- nil because we can't use pre or rft or info: separator = '&nbsp;', }, ['ISSN'] = { parameters = {'issn', 'ISSN'}, link = 'International Standard Serial Number', redirect = 'ISSN (identifier)', q = 'Q131276', label = 'ISSN', prefix = 'https://www.worldcat.org/issn/', COinS = 'rft.issn', encode = false, separator = '&nbsp;', }, ['JFM'] = { parameters = {'jfm', 'JFM'}, link = 'Jahrbuch über die Fortschritte der Mathematik', redirect = 'JFM (identifier)', q = '', label = 'JFM', prefix = 'https://zbmath.org/?format=complete&q=an:', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', }, ['JSTOR'] = { parameters = {'jstor', 'JSTOR'}, link = 'JSTOR', redirect = 'JSTOR (identifier)', q = 'Q1420342', label = 'JSTOR', prefix = 'https://www.jstor.org/stable/', -- protocol-relative tested 2013-09-04 COinS = 'pre', -- use prefix value encode = false, separator = '&nbsp;', custom_access = 'jstor-access', }, ['LCCN'] = { parameters = {'lccn', 'LCCN'}, link = 'Library of Congress Control Number', redirect = 'LCCN (identifier)', q = 'Q620946', label = 'LCCN', prefix = 'https://lccn.loc.gov/', -- protocol-relative tested 2015-12-28 COinS = 'info:lccn', encode = false, separator = '&nbsp;', }, ['MR'] = { parameters = {'mr', 'MR'}, link = 'Mathematical Reviews', redirect = 'MR (identifier)', q = 'Q211172', label = 'MR', prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', }, ['OCLC'] = { parameters = {'oclc', 'OCLC'}, link = 'OCLC', redirect = 'OCLC (identifier)', q = 'Q190593', label = 'OCLC', prefix = 'https://www.worldcat.org/oclc/', COinS = 'info:oclcnum', encode = true, separator = '&nbsp;', id_limit = 9999999999, -- 10-digits }, ['OL'] = { parameters = { 'ol', 'OL' }, link = 'Open Library', redirect = 'OL (identifier)', q = 'Q1201876', label = 'OL', prefix = 'https://openlibrary.org/', COinS = 'url', separator = '&nbsp;', encode = true, custom_access = 'ol-access', }, ['OSTI'] = { parameters = {'osti', 'OSTI'}, link = 'Office of Scientific and Technical Information', redirect = 'OSTI (identifier)', q = 'Q2015776', label = 'OSTI', prefix = 'https://www.osti.gov/biblio/', -- protocol-relative tested 2018-09-12 COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', id_limit = 23010000, custom_access = 'osti-access', }, ['PMC'] = { parameters = {'pmc', 'PMC'}, link = 'PubMed Central', redirect = 'PMC (identifier)', q = 'Q229883', label = 'PMC', prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC', suffix = '', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', id_limit = 10500000, access = 'free', -- free to read }, ['PMID'] = { parameters = {'pmid', 'PMID'}, link = 'PubMed Identifier', redirect = 'PMID (identifier)', q = 'Q2082879', label = 'PMID', prefix = 'https://pubmed.ncbi.nlm.nih.gov/', COinS = 'info:pmid', encode = false, separator = '&nbsp;', id_limit = 37900000, }, ['RFC'] = { parameters = {'rfc', 'RFC'}, link = 'Request for Comments', redirect = 'RFC (identifier)', q = 'Q212971', label = 'RFC', prefix = 'https://tools.ietf.org/html/rfc', COinS = 'pre', -- use prefix value encode = false, separator = '&nbsp;', id_limit = 9300, access = 'free', -- free to read }, ['SBN'] = { parameters = {'sbn', 'SBN'}, link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History redirect = 'SBN (identifier)', label = 'SBN', prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn COinS = nil, -- nil because we can't use pre or rft or info: separator = '&nbsp;', }, ['SSRN'] = { parameters = {'ssrn', 'SSRN'}, link = 'Social Science Research Network', redirect = 'SSRN (identifier)', q = 'Q7550801', label = 'SSRN', prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', id_limit = 4500000, custom_access = 'ssrn-access', }, ['S2CID'] = { parameters = {'s2cid', 'S2CID'}, link = 'Semantic Scholar', redirect = 'S2CID (identifier)', q = 'Q22908627', label = 'S2CID', prefix = 'https://api.semanticscholar.org/CorpusID:', COinS = 'pre', -- use prefix value encode = false, separator = '&nbsp;', id_limit = 260000000, custom_access = 's2cid-access', }, ['USENETID'] = { parameters = {'message-id'}, link = 'Usenet', redirect = 'Usenet (identifier)', q = 'Q193162', label = 'Usenet:', prefix = 'news:', encode = false, COinS = 'pre', -- use prefix value separator = '&nbsp;', }, ['ZBL'] = { parameters = {'zbl', 'ZBL' }, link = 'Zentralblatt MATH', redirect = 'Zbl (identifier)', q = 'Q190269', label = 'Zbl', prefix = 'https://zbmath.org/?format=complete&q=an:', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', }, } --[[--------------------------< E X P O R T S >--------------------------------- ]] return { use_identifier_redirects = true, -- when true use redirect name for identifier label links; always true at en.wiki local_lang_cat_enable = false; -- when true categorizes pages where |language=<local wiki's language>; always false at en.wiki date_name_auto_xlate_enable = false; -- when true translates English month-names to the local-wiki's language month names; always false at en.wiki date_digit_auto_xlate_enable = false; -- when true translates Western date digit to the local-wiki's language digits (date_names['local_digits']); always false at en.wiki -- tables and variables created when this module is loaded global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format. punct_skip = build_skip_table (punct_skip, punct_meta_params), url_skip = build_skip_table (url_skip, url_meta_params), aliases = aliases, special_case_translation = special_case_translation, date_names = date_names, err_msg_supl = err_msg_supl, error_conditions = error_conditions, editor_markup_patterns = editor_markup_patterns, et_al_patterns = et_al_patterns, id_handlers = id_handlers, keywords_lists = keywords_lists, keywords_xlate = keywords_xlate, stripmarkers = stripmarkers, invisible_chars = invisible_chars, invisible_defs = invisible_defs, indic_script = indic_script, emoji_t = emoji_t, maint_cats = maint_cats, messages = messages, presentation = presentation, prop_cats = prop_cats, script_lang_codes = script_lang_codes, lang_code_remap = lang_code_remap, lang_name_remap = lang_name_remap, this_wiki_code = this_wiki_code, title_types = title_types, uncategorized_namespaces = uncategorized_namespaces_t, uncategorized_subpages = uncategorized_subpages, templates_using_volume = templates_using_volume, templates_using_issue = templates_using_issue, templates_not_using_page = templates_not_using_page, vol_iss_pg_patterns = vol_iss_pg_patterns, single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t, inter_wiki_map = inter_wiki_map, mw_languages_by_tag_t = mw_languages_by_tag_t, mw_languages_by_name_t = mw_languages_by_name_t, citation_class_map_t = citation_class_map_t, citation_issue_t = citation_issue_t, citation_no_volume_t = citation_no_volume_t, } ca84924af6925d67aa96e5d4f583c17bd12f69a1 Template:Moby game 10 474 948 2023-07-05T22:35:37Z wikipedia>SWinxy 0 Modifying [[WP:RCAT|redirect categories]] using [[User:Wugapodes/Capricorn|Capricorn ♑]] wikitext text/x-wiki #REDIRECT [[Template:MobyGames]] {{Redirect category shell| {{R from move}} }} 7729f8c0b996558d262215e400c5722da4270669 Module:Transclusion count/data/D 828 374 744 2023-07-09T05:10:36Z wikipedia>Ahechtbot 0 [[Wikipedia:BOT|Bot]]: Updated page. Scribunto text/plain return { ["D&D"] = 3900, ["D&D_to-do"] = 5600, ["D-da"] = 2600, ["DANFS"] = 8400, ["DC-Comics-trademark-copyright"] = 2100, ["DCS_Sri_Lanka"] = 2100, ["DDR"] = 2800, ["DEC"] = 8700, ["DECADE"] = 282000, ["DEN"] = 6700, ["DEU"] = 17000, ["DMC"] = 59000, ["DMCA"] = 2100000, ["DNB"] = 8800, ["DNB-Portal"] = 2100, ["DNB_portal"] = 3800, ["DNK"] = 7700, ["DNZB"] = 3600, ["DOM"] = 2100, ["DOWs"] = 2600, ["DRV_links"] = 3600, ["DWT"] = 2300, ["DYKC"] = 6700, ["DYKF"] = 2100, ["DYK_blue"] = 3400, ["DYK_checklist"] = 13000, ["DYK_conditions"] = 65000, ["DYK_files"] = 2100, ["DYK_header"] = 33000, ["DYK_nompage_links"] = 77000, ["DYK_talk"] = 102000, ["DYK_talk/date"] = 102000, ["DYK_tools"] = 51000, ["DYK_tools/styles.css"] = 51000, ["DYKfile"] = 12000, ["DZA"] = 2700, ["Dab"] = 18000, ["Dablink"] = 2100, ["Dagger"] = 18000, ["Dashboard.wikiedu.org_assignment"] = 13000, ["Dashboard.wikiedu.org_bibliography/guide"] = 6400, ["Dashboard.wikiedu.org_course_header"] = 6000, ["Dashboard.wikiedu.org_course_header/edit-note"] = 6000, ["Dashboard.wikiedu.org_draft_template/about_this_sandbox"] = 9800, ["Dashboard.wikiedu.org_evaluate_article/guide"] = 7300, ["Dashboard.wikiedu.org_peer_review/guide"] = 12000, ["Dashboard.wikiedu.org_sandbox"] = 92000, ["Dashboard.wikiedu.org_student_editor"] = 79000, ["Dashboard.wikiedu.org_student_program_sandbox"] = 91000, ["Dashboard.wikiedu.org_talk_course_link"] = 81000, ["Dashboard.wikiedu.org_user_talk"] = 2200, ["Date"] = 41000, ["Date-mf"] = 38000, ["Date_table_sorting"] = 39000, ["Dated_maintenance_category"] = 2160000, ["Dated_maintenance_category_(articles)"] = 2100000, ["Dated_maintenance_category_by_type_(articles)"] = 21000, ["Davis_Cup_player"] = 2500, ["Day+1"] = 6900, ["Day-1"] = 8300, ["Dbox"] = 3000, ["Dda"] = 5200, ["Dead_YouTube_link"] = 2700, ["Dead_Youtube_links"] = 2400, ["Dead_link"] = 278000, ["Death-date"] = 11000, ["Death-date_and_age"] = 10000, ["Death_date"] = 9100, ["Death_date_and_age"] = 383000, ["Death_date_and_given_age"] = 2600, ["Death_year_and_age"] = 16000, ["Death_year_category_header"] = 2000, ["Decade"] = 2100, ["Decade_link"] = 32000, ["Decimals"] = 3000, ["Decline"] = 2500, ["Declined"] = 3200, ["Decrease"] = 34000, ["Define"] = 5100, ["Deg2DMS"] = 3400, ["Deletion_review_log_header"] = 5600, ["Deletion_review_log_header/Core"] = 5600, ["Delink"] = 1970000, ["Delink_question_hyphen-minus"] = 293000, ["Delrevxfd"] = 3500, ["Democratic_Party_(US)/meta/shading"] = 15000, ["Description_missing"] = 6800, ["Designation/abbreviation"] = 8100, ["Designation/color"] = 76000, ["Designation/colour"] = 79000, ["Designation/colour2"] = 18000, ["Designation/divbox"] = 29000, ["Designation/text"] = 42000, ["Designation_list"] = 5300, ["Details"] = 4700, ["DetailsLink"] = 5800, ["Detect_singular"] = 198000, ["Deutsche_Bahn_station_codes"] = 2200, ["DfE_performance_tables"] = 4500, ["Diff"] = 30000, ["Diff2"] = 12000, ["Digits"] = 21000, ["Directories_box"] = 3200, ["Disamb"] = 2200, ["Disambig"] = 63000, ["Disambig-Class"] = 12000, ["DisambigProj"] = 12000, ["DisambigProject"] = 150000, ["Disambigproject"] = 5800, ["Disambiguation"] = 217000, ["Disambiguation/cat"] = 216000, ["Disambiguation_page_short_description"] = 351000, ["Discogs_artist"] = 16000, ["Discogs_master"] = 12000, ["Discogs_release"] = 3000, ["Discussion_bottom"] = 12000, ["Discussion_top"] = 12000, ["DisestcatCountry"] = 9500, ["DisestcatCountry/core"] = 9500, ["DisestcatCountryDecade"] = 2600, ["DisestcatUSstate"] = 4900, ["DisestcatUSstate/core"] = 4900, ["Disputed"] = 2200, ["Distinguish"] = 95000, ["Disused_Rail_Start"] = 3900, ["Disused_rail_start"] = 4200, ["Disused_style"] = 4600, ["Div_col"] = 397000, ["Div_col/styles.css"] = 399000, ["Div_col_end"] = 299000, ["Div_col_start"] = 2900, ["Div_end"] = 2700, ["Divbox"] = 337000, ["Divbox/styles.css"] = 360000, ["Dividing_line"] = 4000, ["Dmbox"] = 458000, ["Dmbox/styles.css"] = 458000, ["Do_not_move_to_Commons"] = 16000, ["Doc"] = 3500, ["Documentation"] = 89000, ["Documentation_subpage"] = 95000, ["Dog_opentask"] = 3400, ["Doi"] = 25000, ["Doing"] = 3600, ["Don't_edit_this_line"] = 107000, ["Don't_edit_this_line_always_display"] = 472000, ["Don't_edit_this_line_extinct"] = 472000, ["Don't_edit_this_line_link_target"] = 472000, ["Don't_edit_this_line_link_text"] = 472000, ["Don't_edit_this_line_parent"] = 472000, ["Don't_edit_this_line_rank"] = 472000, ["Don't_edit_this_line_refs"] = 107000, ["Don't_edit_this_line_same_as"] = 472000, ["Done"] = 101000, ["Doppelganger"] = 2800, ["Dot"] = 2700, ["Double+single"] = 2700, ["Double-dagger"] = 19000, ["Dr"] = 3400, ["Dr-logno"] = 3400, ["Dr-make"] = 3400, ["Dr-yr"] = 3400, ["Draft"] = 4200, ["Draft_article"] = 6200, ["Draft_article_check"] = 6300, ["Draft_categories"] = 6900, ["Draft_other"] = 114000, ["Draft_topics"] = 25000, ["Drafts_moved_from_mainspace"] = 11000, ["Draw"] = 3800, ["Draw_key"] = 18000, ["Draw_links"] = 13000, ["Drep"] = 3400, ["Drugbankcite"] = 4300, ["Drugbox"] = 7500, ["Drugs.com"] = 3600, ["Ds/talk_notice"] = 6200, ["Dts"] = 38000, ["Dubious"] = 8400, ["Duck"] = 2900, ["Dummytab"] = 4800, ["Duration"] = 38000, ["Dutch_municipality"] = 2600, ["Dyktalk"] = 42000, ["Dynamic_list"] = 9800, ["Module:DYK_checklist"] = 13000, ["Module:DYK_checklist/data"] = 13000, ["Module:DYK_nompage_links"] = 77000, ["Module:Data"] = 148000, ["Module:Date"] = 1360000, ["Module:DateI18n"] = 65000, ["Module:Date_table_sorting"] = 39000, ["Module:DecodeEncode"] = 114000, ["Module:Delink"] = 2060000, ["Module:Detect_singular"] = 1500000, ["Module:Disambiguation"] = 2840000, ["Module:Distinguish"] = 95000, ["Module:Documentation"] = 135000, ["Module:Documentation/config"] = 135000, ["Module:Documentation/styles.css"] = 134000, ["Module:Draft_topics"] = 25000, ["Module:Duration"] = 237000, } 5fe4d8676a43df3290b3d4ff79f8604c8ae2a930 Module:Transclusion count/data/I 828 386 772 2023-07-09T05:11:26Z wikipedia>Ahechtbot 0 [[Wikipedia:BOT|Bot]]: Updated page. Scribunto text/plain return { ["IAAF_name"] = 2200, ["IAST"] = 6000, ["IBDB_name"] = 9100, ["ICD10"] = 4700, ["ICD9"] = 4400, ["ICS"] = 2900, ["IDN"] = 3300, ["IMDb_episode"] = 9900, ["IMDb_episodes"] = 2600, ["IMDb_name"] = 152000, ["IMDb_title"] = 187000, ["IMO_Number"] = 4100, ["IMSLP"] = 8200, ["INA"] = 2100, ["IND"] = 7400, ["INR"] = 6400, ["INRConvert"] = 5600, ["INRConvert/CurrentRate"] = 5500, ["INRConvert/USD"] = 5500, ["INRConvert/out"] = 5500, ["IOC_profile"] = 5300, ["IP"] = 2600, ["IPA"] = 141000, ["IPA-all"] = 3600, ["IPA-de"] = 8100, ["IPA-es"] = 8000, ["IPA-fr"] = 44000, ["IPA-it"] = 5900, ["IPA-nl"] = 3700, ["IPA-pl"] = 4000, ["IPA-pt"] = 3700, ["IPA-ru"] = 2700, ["IPA-sh"] = 2700, ["IPA-sl"] = 6900, ["IPA-th"] = 3000, ["IPA_audio_link"] = 19000, ["IPA_link"] = 3400, ["IPAc-cmn"] = 2600, ["IPAc-en"] = 48000, ["IPAc-pl"] = 52000, ["IPC_athlete"] = 2800, ["IPSummary"] = 78000, ["IP_summary"] = 78000, ["IPtalk"] = 22000, ["IPuser"] = 7000, ["IPvandal"] = 2700, ["IRC"] = 7300, ["IRI"] = 2200, ["IRL"] = 5400, ["IRN"] = 3500, ["ISBN"] = 461000, ["ISBNT"] = 38000, ["ISBN_missing"] = 2400, ["ISFDB_name"] = 4100, ["ISFDB_title"] = 4600, ["ISL"] = 2100, ["ISO_15924/script-example-character"] = 2800, ["ISO_15924/wp-article"] = 2800, ["ISO_15924/wp-article/format"] = 2800, ["ISO_15924/wp-article/label"] = 2700, ["ISO_3166_code"] = 511000, ["ISO_3166_name"] = 16000, ["ISO_639_name"] = 8000, ["ISP"] = 5300, ["ISR"] = 4700, ["ISSN"] = 12000, ["ISSN_link"] = 30000, ["ISTAT"] = 8100, ["ISU_figure_skater"] = 2500, ["ITA"] = 17000, ["ITF"] = 6100, ["ITF_profile"] = 9000, ["ITIS"] = 4400, ["ITN_talk"] = 9900, ["ITN_talk/date"] = 9900, ["IUCN_banner"] = 15000, ["I_sup"] = 4500, ["Iaaf_name"] = 7400, ["Ice_hockey"] = 19000, ["Ice_hockey_stats"] = 16000, ["Icehockeystats"] = 12000, ["Icon"] = 575000, ["If"] = 267000, ["If_all"] = 6300, ["If_between"] = 3700, ["If_both"] = 3040000, ["If_empty"] = 3620000, ["If_first_display_both"] = 73000, ["If_in_page"] = 9100, ["If_last_display_both"] = 30000, ["If_preview"] = 57000, ["If_then_show"] = 281000, ["Ifempty"] = 4200, ["Ifeq"] = 16000, ["Iferror_then_show"] = 3200, ["Ifexist_not_redirect"] = 1120000, ["Ifnotempty"] = 14000, ["Ifnumber"] = 35000, ["Ifsubst"] = 433000, ["Ih"] = 7500, ["Ill"] = 112000, ["Illm"] = 6800, ["Image_frame"] = 4900, ["Image_label"] = 4500, ["Image_label_begin"] = 3800, ["Image_label_end"] = 3500, ["Image_label_small"] = 2600, ["Image_needed"] = 4500, ["Image_other"] = 277000, ["Image_requested"] = 166000, ["Image_requested/Category_helper"] = 158000, ["Imbox"] = 915000, ["Imdb_name"] = 5300, ["Imdb_title"] = 4200, ["Import_style"] = 11000, ["Import_style/inputbox.css"] = 11000, ["Importance"] = 5580000, ["Importance/colour"] = 5590000, ["Importance_mask"] = 9620000, ["Improve_categories"] = 7300, ["Improve_documentation"] = 2200, ["In_class"] = 5700, ["In_lang"] = 353000, ["In_progress"] = 3200, ["In_string"] = 73000, ["In_title"] = 19000, ["Inactive_WikiProject_banner"] = 208000, ["Inactive_userpage_blanked"] = 4900, ["Include-USGov"] = 29000, ["Incomplete_list"] = 23000, ["Inconclusive"] = 2100, ["Increase"] = 42000, ["Incumbent_pope"] = 4300, ["Indent"] = 4200, ["IndexFungorum"] = 2200, ["Indian_English"] = 4300, ["Indian_Rupee"] = 10000, ["Indian_railway_code"] = 3100, ["Inflation"] = 19000, ["Inflation-fn"] = 5400, ["Inflation-year"] = 4400, ["Inflation/IN/startyear"] = 5500, ["Inflation/UK"] = 4300, ["Inflation/UK/dataset"] = 4300, ["Inflation/UK/startyear"] = 4300, ["Inflation/US"] = 12000, ["Inflation/US/dataset"] = 12000, ["Inflation/US/startyear"] = 12000, ["Inflation/fn"] = 6100, ["Inflation/year"] = 24000, ["Info"] = 7200, ["Infobox"] = 3200000, ["Infobox/Columns"] = 2400, ["Infobox/mobileviewfix.css"] = 142000, ["Infobox3cols"] = 16000, ["Infobox_AFL_biography"] = 14000, ["Infobox_Aircraft_Begin"] = 5400, ["Infobox_Aircraft_Type"] = 4700, ["Infobox_Athletics_Championships"] = 2700, ["Infobox_Australian_place"] = 15000, ["Infobox_CFL_biography"] = 2200, ["Infobox_COA_wide"] = 3100, ["Infobox_Canada_electoral_district"] = 2400, ["Infobox_Canadian_Football_League_biography"] = 5800, ["Infobox_Canadian_Football_League_biography/position"] = 5700, ["Infobox_Chinese"] = 20000, ["Infobox_Chinese/Chinese"] = 2700, ["Infobox_Chinese/Footer"] = 8700, ["Infobox_Chinese/Header"] = 8700, ["Infobox_Chinese/Korean"] = 17000, ["Infobox_Christian_leader"] = 18000, ["Infobox_Election"] = 2200, ["Infobox_French_commune"] = 38000, ["Infobox_GAA_player"] = 2800, ["Infobox_Gaelic_games_player"] = 5000, ["Infobox_German_location"] = 13000, ["Infobox_German_place"] = 14000, ["Infobox_Grand_Prix_race_report"] = 2000, ["Infobox_Greece_place"] = 2800, ["Infobox_Greek_Dimos"] = 2800, ["Infobox_Hindu_temple"] = 2400, ["Infobox_Indian_constituency"] = 4600, ["Infobox_Indian_constituency/defaultdata"] = 4600, ["Infobox_Italian_comune"] = 8100, ["Infobox_Korean_name"] = 15000, ["Infobox_Korean_name/categories"] = 15000, ["Infobox_MLB_yearly"] = 3100, ["Infobox_NASCAR_race_report"] = 2100, ["Infobox_NCAA_team_season"] = 18000, ["Infobox_NFL_biography"] = 28000, ["Infobox_NFL_player"] = 7900, ["Infobox_NFL_season"] = 2500, ["Infobox_NFL_team_season"] = 3900, ["Infobox_NRHP"] = 72000, ["Infobox_NRHP/conv"] = 18000, ["Infobox_NRHP/locmapin2region"] = 66000, ["Infobox_Officeholder"] = 4900, ["Infobox_Olympic_event"] = 7300, ["Infobox_Olympic_event/games_text"] = 7300, ["Infobox_Paralympic_event"] = 2600, ["Infobox_Paralympic_event/games_text"] = 2600, ["Infobox_Politician"] = 2200, ["Infobox_Romanian_subdivision"] = 3100, ["Infobox_Russian_district"] = 2000, ["Infobox_Russian_inhabited_locality"] = 4400, ["Infobox_SCOTUS_case"] = 3700, ["Infobox_Site_of_Special_Scientific_Interest"] = 2000, ["Infobox_Swiss_town"] = 2800, ["Infobox_Switzerland_municipality"] = 2900, ["Infobox_Turkey_place"] = 14000, ["Infobox_U.S._county"] = 3000, ["Infobox_U.S._county/district"] = 3000, ["Infobox_UK_constituency"] = 2100, ["Infobox_UK_constituency/year"] = 2100, ["Infobox_UK_legislation"] = 3100, ["Infobox_UK_place"] = 26000, ["Infobox_UK_place/NoDialCode"] = 7900, ["Infobox_UK_place/NoPostCode"] = 3000, ["Infobox_UK_place/area"] = 2400, ["Infobox_UK_place/dist"] = 2500, ["Infobox_UK_place/local"] = 26000, ["Infobox_UK_place/styles.css"] = 26000, ["Infobox_UN_resolution"] = 2300, ["Infobox_US_Supreme_Court_case"] = 3800, ["Infobox_US_Supreme_Court_case/courts"] = 3800, ["Infobox_Wikipedia_user"] = 9600, ["Infobox_YouTube_personality"] = 2600, ["Infobox_YouTube_personality/styles.css"] = 2600, ["Infobox_academic"] = 13000, ["Infobox_aircraft_begin"] = 14000, ["Infobox_aircraft_occurrence"] = 2300, ["Infobox_aircraft_type"] = 12000, ["Infobox_airline"] = 4600, ["Infobox_airport"] = 15000, ["Infobox_airport/datatable"] = 15000, ["Infobox_album"] = 161000, ["Infobox_album/color"] = 190000, ["Infobox_album/link"] = 161000, ["Infobox_anatomy"] = 4400, ["Infobox_ancient_site"] = 5300, ["Infobox_animanga/Footer"] = 6700, ["Infobox_animanga/Header"] = 6700, ["Infobox_animanga/Print"] = 5400, ["Infobox_animanga/Video"] = 4700, ["Infobox_architect"] = 3700, ["Infobox_artist"] = 28000, ["Infobox_artist_discography"] = 5900, ["Infobox_artwork"] = 11000, ["Infobox_athlete"] = 3000, ["Infobox_automobile"] = 8400, ["Infobox_award"] = 13000, ["Infobox_badminton_player"] = 3200, ["Infobox_baseball_biography"] = 28000, ["Infobox_baseball_biography/style"] = 28000, ["Infobox_baseball_biography/styles.css"] = 28000, ["Infobox_basketball_biography"] = 21000, ["Infobox_basketball_biography/style"] = 21000, ["Infobox_basketball_club"] = 3000, ["Infobox_beauty_pageant"] = 2400, ["Infobox_bilateral_relations"] = 4300, ["Infobox_body_of_water"] = 18000, ["Infobox_book"] = 52000, ["Infobox_boxer"] = 5700, ["Infobox_bridge"] = 6000, ["Infobox_building"] = 27000, ["Infobox_character"] = 7600, ["Infobox_chess_biography"] = 3700, ["Infobox_chess_player"] = 3100, ["Infobox_church"] = 15000, ["Infobox_church/denomination"] = 15000, ["Infobox_church/font_color"] = 15000, ["Infobox_civil_conflict"] = 2300, ["Infobox_civilian_attack"] = 5400, ["Infobox_college_coach"] = 11000, ["Infobox_college_football_game"] = 2100, ["Infobox_college_sports_team_season"] = 39000, ["Infobox_college_sports_team_season/link"] = 39000, ["Infobox_college_sports_team_season/name"] = 39000, ["Infobox_college_sports_team_season/succession"] = 39000, ["Infobox_college_sports_team_season/team"] = 39000, ["Infobox_comic_book_title"] = 2900, ["Infobox_comics_character"] = 3600, ["Infobox_comics_creator"] = 3500, ["Infobox_comics_creator/styles.css"] = 3500, ["Infobox_company"] = 83000, ["Infobox_computing_device"] = 2300, ["Infobox_concert"] = 3200, ["Infobox_constituency"] = 5100, ["Infobox_country"] = 6300, ["Infobox_country/formernext"] = 6000, ["Infobox_country/imagetable"] = 5200, ["Infobox_country/multirow"] = 8200, ["Infobox_country/status_text"] = 2700, ["Infobox_country/styles.css"] = 6300, ["Infobox_country_at_games"] = 15000, ["Infobox_country_at_games/core"] = 15000, ["Infobox_country_at_games/see_also"] = 12000, ["Infobox_court_case"] = 4600, ["Infobox_court_case/images"] = 2400, ["Infobox_cricket_tournament"] = 2300, ["Infobox_cricketer"] = 32000, ["Infobox_cricketer/career"] = 32000, ["Infobox_cricketer/national_side"] = 7500, ["Infobox_criminal"] = 6300, ["Infobox_curler"] = 2600, ["Infobox_cycling_race_report"] = 4500, ["Infobox_cyclist"] = 16000, ["Infobox_dam"] = 5600, ["Infobox_designation_list"] = 19000, ["Infobox_designation_list/entry"] = 17000, ["Infobox_dim"] = 6900, ["Infobox_dim/core"] = 6900, ["Infobox_diocese"] = 3800, ["Infobox_drug"] = 9500, ["Infobox_drug/chemical_formula"] = 9500, ["Infobox_drug/data_page_link"] = 9500, ["Infobox_drug/formatATC"] = 9400, ["Infobox_drug/formatCASnumber"] = 9500, ["Infobox_drug/formatChEBI"] = 9500, ["Infobox_drug/formatChEMBL"] = 9500, ["Infobox_drug/formatChemDBNIAID"] = 9500, ["Infobox_drug/formatChemSpider"] = 9500, ["Infobox_drug/formatCompTox"] = 9500, ["Infobox_drug/formatDrugBank"] = 9500, ["Infobox_drug/formatIUPHARBPS"] = 9500, ["Infobox_drug/formatJmol"] = 9500, ["Infobox_drug/formatKEGG"] = 9500, ["Infobox_drug/formatPDBligand"] = 8800, ["Infobox_drug/formatPubChemCID"] = 9500, ["Infobox_drug/formatPubChemSID"] = 9500, ["Infobox_drug/formatUNII"] = 9500, ["Infobox_drug/legal_status"] = 9600, ["Infobox_drug/licence"] = 9600, ["Infobox_drug/maintenance_categories"] = 9500, ["Infobox_drug/non-ref-space"] = 3900, ["Infobox_drug/pregnancy_category"] = 9600, ["Infobox_drug/title"] = 9500, ["Infobox_election"] = 29000, ["Infobox_election/row"] = 29000, ["Infobox_election/shortname"] = 28000, ["Infobox_enzyme"] = 5100, ["Infobox_ethnic_group"] = 7200, ["Infobox_event"] = 5300, ["Infobox_family"] = 2100, ["Infobox_figure_skater"] = 4200, ["Infobox_film"] = 155000, ["Infobox_film/short_description"] = 151000, ["Infobox_film_awards"] = 2600, ["Infobox_film_awards/link"] = 2600, ["Infobox_film_awards/style"] = 2600, ["Infobox_food"] = 6800, ["Infobox_football_biography"] = 205000, ["Infobox_football_club"] = 27000, ["Infobox_football_club_season"] = 20000, ["Infobox_football_league"] = 2500, ["Infobox_football_league_season"] = 19000, ["Infobox_football_match"] = 5800, ["Infobox_football_tournament_season"] = 8000, ["Infobox_former_subdivision"] = 3300, ["Infobox_former_subdivision/styles.css"] = 3300, ["Infobox_galaxy"] = 2100, ["Infobox_game"] = 2500, ["Infobox_game_score"] = 3400, ["Infobox_gene"] = 13000, ["Infobox_given_name"] = 4000, ["Infobox_golfer"] = 4400, ["Infobox_golfer/highest_ranking"] = 4400, ["Infobox_government_agency"] = 10000, ["Infobox_government_cabinet"] = 2500, ["Infobox_gridiron_football_person"] = 2500, ["Infobox_gridiron_football_person/position"] = 5700, ["Infobox_gymnast"] = 3400, ["Infobox_handball_biography"] = 4900, ["Infobox_historic_site"] = 11000, ["Infobox_horseraces"] = 2600, ["Infobox_hospital"] = 6300, ["Infobox_hospital/care_system"] = 6300, ["Infobox_hospital/lists"] = 6300, ["Infobox_ice_hockey_biography"] = 20000, ["Infobox_ice_hockey_player"] = 19000, ["Infobox_ice_hockey_team"] = 3000, ["Infobox_ice_hockey_team_season"] = 2000, ["Infobox_international_football_competition"] = 5700, ["Infobox_islands"] = 8700, ["Infobox_islands/area"] = 9100, ["Infobox_islands/density"] = 9100, ["Infobox_islands/length"] = 8700, ["Infobox_islands/styles.css"] = 8700, ["Infobox_journal"] = 9700, ["Infobox_journal/Abbreviation_search"] = 9600, ["Infobox_journal/Bluebook_check"] = 9400, ["Infobox_journal/Former_check"] = 9400, ["Infobox_journal/ISO_4_check"] = 9400, ["Infobox_journal/ISSN-eISSN"] = 9400, ["Infobox_journal/Indexing_search"] = 9500, ["Infobox_journal/MathSciNet_check"] = 9400, ["Infobox_journal/NLM_check"] = 9400, ["Infobox_journal/frequency"] = 8600, ["Infobox_lake"] = 4400, ["Infobox_language"] = 9500, ["Infobox_language/family-color"] = 11000, ["Infobox_language/genetic"] = 6500, ["Infobox_language/linguistlist"] = 9500, ["Infobox_language/ref"] = 7100, ["Infobox_legislature"] = 3600, ["Infobox_library"] = 2100, ["Infobox_lighthouse"] = 2600, ["Infobox_lighthouse/light"] = 2600, ["Infobox_locomotive"] = 4900, ["Infobox_magazine"] = 7600, ["Infobox_manner_of_address"] = 3300, ["Infobox_mapframe"] = 79000, ["Infobox_martial_artist"] = 5700, ["Infobox_martial_artist/record"] = 5700, ["Infobox_medal_templates"] = 420000, ["Infobox_medical_condition"] = 10000, ["Infobox_medical_condition_(new)"] = 8200, ["Infobox_medical_details"] = 2000, ["Infobox_military_conflict"] = 22000, ["Infobox_military_installation"] = 9700, ["Infobox_military_person"] = 44000, ["Infobox_military_unit"] = 26000, ["Infobox_mine"] = 2100, ["Infobox_model"] = 2300, ["Infobox_mountain"] = 28000, ["Infobox_multi-sport_competition_event"] = 2300, ["Infobox_museum"] = 10000, ["Infobox_musical_artist"] = 121000, ["Infobox_musical_artist/color"] = 121000, ["Infobox_musical_artist/hCard_class"] = 311000, ["Infobox_musical_composition"] = 2800, ["Infobox_name"] = 7400, ["Infobox_name_module"] = 6700, ["Infobox_newspaper"] = 9600, ["Infobox_nobility"] = 2400, ["Infobox_noble"] = 7200, ["Infobox_officeholder"] = 214000, ["Infobox_officeholder/office"] = 220000, ["Infobox_official_post"] = 7900, ["Infobox_organization"] = 36000, ["Infobox_pageant_titleholder"] = 2800, ["Infobox_park"] = 7300, ["Infobox_person"] = 467000, ["Infobox_person/Wikidata"] = 4900, ["Infobox_person/height"] = 101000, ["Infobox_person/length"] = 7000, ["Infobox_person/weight"] = 66000, ["Infobox_philosopher"] = 3300, ["Infobox_planet"] = 4700, ["Infobox_play"] = 3800, ["Infobox_political_party"] = 14000, ["Infobox_power_station"] = 3000, ["Infobox_prepared_food"] = 3100, ["Infobox_professional_wrestler"] = 4200, ["Infobox_professional_wrestling_event"] = 2600, ["Infobox_protected_area"] = 14000, ["Infobox_protein_family"] = 2100, ["Infobox_publisher"] = 2400, ["Infobox_racehorse"] = 5500, ["Infobox_racing_driver"] = 3800, ["Infobox_radio_station"] = 22000, ["Infobox_rail"] = 2900, ["Infobox_rail_line"] = 7200, ["Infobox_rail_line/tracking"] = 7200, ["Infobox_rail_service"] = 2900, ["Infobox_rail_service/doc"] = 2900, ["Infobox_reality_competition_season"] = 3400, ["Infobox_record_label"] = 4000, ["Infobox_recurring_event"] = 6300, ["Infobox_religious_biography"] = 5100, ["Infobox_religious_building"] = 12000, ["Infobox_religious_building/color"] = 17000, ["Infobox_restaurant"] = 2500, ["Infobox_river"] = 30000, ["Infobox_river/calcunit"] = 30000, ["Infobox_river/discharge"] = 30000, ["Infobox_river/row-style"] = 30000, ["Infobox_river/source"] = 30000, ["Infobox_road"] = 24000, ["Infobox_road/meta/mask/category"] = 23000, ["Infobox_road/meta/mask/country"] = 24000, ["Infobox_road/styles.css"] = 25000, ["Infobox_road_small"] = 2300, ["Infobox_rockunit"] = 6400, ["Infobox_royalty"] = 21000, ["Infobox_royalty/short_description"] = 13000, ["Infobox_rugby_biography"] = 16000, ["Infobox_rugby_biography/correct_date"] = 16000, ["Infobox_rugby_biography/depcheck"] = 15000, ["Infobox_rugby_league_biography"] = 9900, ["Infobox_rugby_league_biography/PLAYER"] = 9800, ["Infobox_rugby_team"] = 2600, ["Infobox_sailboat_specifications"] = 2300, ["Infobox_saint"] = 4900, ["Infobox_school"] = 38000, ["Infobox_school/short_description"] = 38000, ["Infobox_school_district"] = 5600, ["Infobox_school_district/styles.css"] = 5600, ["Infobox_scientist"] = 48000, ["Infobox_service_record"] = 2600, ["Infobox_settlement"] = 559000, ["Infobox_settlement/areadisp"] = 233000, ["Infobox_settlement/columns"] = 94000, ["Infobox_settlement/columns/styles.css"] = 94000, ["Infobox_settlement/densdisp"] = 432000, ["Infobox_settlement/impus"] = 81000, ["Infobox_settlement/lengthdisp"] = 168000, ["Infobox_settlement/link"] = 93000, ["Infobox_settlement/metric"] = 207000, ["Infobox_settlement/pref"] = 288000, ["Infobox_settlement/styles.css"] = 559000, ["Infobox_ship_begin"] = 41000, ["Infobox_ship_career"] = 37000, ["Infobox_ship_characteristics"] = 40000, ["Infobox_ship_class_overview"] = 4100, ["Infobox_ship_image"] = 40000, ["Infobox_shopping_mall"] = 3400, ["Infobox_short_story"] = 2300, ["Infobox_skier"] = 2500, ["Infobox_soap_character"] = 2900, ["Infobox_software"] = 14000, ["Infobox_software/simple"] = 14000, ["Infobox_song"] = 75000, ["Infobox_song/color"] = 75000, ["Infobox_song/link"] = 75000, ["Infobox_spaceflight"] = 3500, ["Infobox_spaceflight/styles.css"] = 3500, ["Infobox_sport_event"] = 2100, ["Infobox_sports_competition_event"] = 16000, ["Infobox_sports_competition_event/medalrow"] = 11000, ["Infobox_sports_league"] = 5000, ["Infobox_sports_season"] = 5300, ["Infobox_sports_team"] = 2200, ["Infobox_sportsperson"] = 106000, ["Infobox_stadium"] = 3500, ["Infobox_station"] = 55000, ["Infobox_station/doc"] = 55000, ["Infobox_station/services"] = 55000, ["Infobox_station/styles.css"] = 55000, ["Infobox_street"] = 3400, ["Infobox_swimmer"] = 9300, ["Infobox_television"] = 56000, ["Infobox_television/Short_description"] = 54000, ["Infobox_television_channel"] = 6200, ["Infobox_television_episode"] = 12000, ["Infobox_television_episode/styles.css"] = 12000, ["Infobox_television_season"] = 9300, ["Infobox_television_station"] = 3700, ["Infobox_tennis_biography"] = 10000, ["Infobox_tennis_event"] = 2400, ["Infobox_tennis_tournament_event"] = 18000, ["Infobox_tennis_tournament_year"] = 9100, ["Infobox_tennis_tournament_year/color"] = 28000, ["Infobox_tennis_tournament_year/footer"] = 28000, ["Infobox_train"] = 2300, ["Infobox_union"] = 2100, ["Infobox_university"] = 26000, ["Infobox_user"] = 2600, ["Infobox_venue"] = 18000, ["Infobox_video_game"] = 28000, ["Infobox_video_game/styles.css"] = 28000, ["Infobox_volleyball_biography"] = 5200, ["Infobox_weapon"] = 7300, ["Infobox_website"] = 7700, ["Infobox_writer"] = 38000, ["Information"] = 103000, ["Information/styles.css"] = 103000, ["Inprogress"] = 2300, ["Input_link"] = 32000, ["Instagram"] = 11000, ["Interlanguage_link"] = 149000, ["Interlanguage_link_multi"] = 19000, ["Internet_Archive_author"] = 18000, ["Internet_Archive_film"] = 2500, ["Intitle"] = 12000, ["Invalid_SVG"] = 3800, ["Invalid_SVG/styles.css"] = 3800, ["Iptalk"] = 21000, ["IranCensus2006"] = 49000, ["IranNCSGN"] = 3200, ["Iran_Census_2006"] = 49000, ["Irc"] = 2100, ["Irish_place_name"] = 2600, ["IsIPAddress"] = 40000, ["IsValidPageName"] = 139000, ["Is_country_in_Central_America"] = 13000, ["Is_country_in_the_Caribbean"] = 14000, ["Is_interwiki_link"] = 6100, ["Is_italic_taxon"] = 472000, ["Is_redirect"] = 26000, ["Isbn"] = 7300, ["Isfdb_name"] = 3800, ["Isfdb_title"] = 4500, ["Isnumeric"] = 204000, ["Iso2continent"] = 35000, ["Iso2country"] = 23000, ["Iso2country/article"] = 22000, ["Iso2country/data"] = 23000, ["Iso2nationality"] = 222000, ["Issubst"] = 71000, ["Isu_name"] = 2200, ["Italic_dab2"] = 5200, ["Italic_title"] = 283000, ["Italic_title_prefixed"] = 8600, ["Italics_colon"] = 3700, ["Italictitle"] = 4300, ["Ivm"] = 5700, ["Ivm/styles.css"] = 5700, ["Ivmbox"] = 122000, ["Ivory_messagebox"] = 139000, ["Module:I18n/complex_date"] = 65000, ["Module:IP"] = 129000, ["Module:IPA_symbol"] = 4700, ["Module:IPA_symbol/data"] = 4700, ["Module:IPAc-en"] = 48000, ["Module:IPAc-en/data"] = 48000, ["Module:IPAc-en/phonemes"] = 48000, ["Module:IPAc-en/pronunciation"] = 48000, ["Module:IPAddress"] = 189000, ["Module:ISO_3166"] = 1020000, ["Module:ISO_3166/data/AT"] = 2500, ["Module:ISO_3166/data/BA"] = 3400, ["Module:ISO_3166/data/CA"] = 2500, ["Module:ISO_3166/data/CN"] = 2100, ["Module:ISO_3166/data/DE"] = 14000, ["Module:ISO_3166/data/ES"] = 3600, ["Module:ISO_3166/data/FR"] = 38000, ["Module:ISO_3166/data/GB"] = 6400, ["Module:ISO_3166/data/GR"] = 3100, ["Module:ISO_3166/data/IN"] = 28000, ["Module:ISO_3166/data/IR"] = 5600, ["Module:ISO_3166/data/National"] = 1020000, ["Module:ISO_3166/data/PL"] = 5800, ["Module:ISO_3166/data/RS"] = 3200, ["Module:ISO_3166/data/RU"] = 24000, ["Module:ISO_3166/data/US"] = 85000, ["Module:ISO_639_name"] = 20000, ["Module:ISOdate"] = 65000, ["Module:Icon"] = 575000, ["Module:Icon/data"] = 575000, ["Module:If_empty"] = 3620000, ["Module:If_in_page"] = 9100, ["Module:If_preview"] = 544000, ["Module:If_preview/configuration"] = 544000, ["Module:If_preview/styles.css"] = 544000, ["Module:Import_style"] = 11000, ["Module:In_lang"] = 353000, ["Module:Indent"] = 4200, ["Module:Infobox"] = 4070000, ["Module:Infobox/dates"] = 66000, ["Module:Infobox/styles.css"] = 4320000, ["Module:Infobox3cols"] = 295000, ["Module:InfoboxImage"] = 4370000, ["Module:Infobox_body_of_water_tracking"] = 18000, ["Module:Infobox_cyclist_tracking"] = 16000, ["Module:Infobox_gene"] = 13000, ["Module:Infobox_mapframe"] = 399000, ["Module:Infobox_military_conflict"] = 22000, ["Module:Infobox_military_conflict/styles.css"] = 22000, ["Module:Infobox_multi-lingual_name"] = 20000, ["Module:Infobox_multi-lingual_name/data"] = 20000, ["Module:Infobox_power_station"] = 3000, ["Module:Infobox_road"] = 25000, ["Module:Infobox_road/browselinks"] = 25000, ["Module:Infobox_road/errors"] = 24000, ["Module:Infobox_road/length"] = 25000, ["Module:Infobox_road/locations"] = 24000, ["Module:Infobox_road/map"] = 24000, ["Module:Infobox_road/route"] = 25000, ["Module:Infobox_road/sections"] = 24000, ["Module:Infobox_television"] = 56000, ["Module:Infobox_television_disambiguation_check"] = 63000, ["Module:Infobox_television_episode"] = 12000, ["Module:Infobox_television_season_disambiguation_check"] = 8900, ["Module:Infobox_television_season_name"] = 9300, ["Module:Internet_Archive"] = 18000, ["Module:IrelandByCountyCatNav"] = 3400, ["Module:Is_infobox_in_lead"] = 376000, ["Module:Is_instance"] = 333000, ["Module:Italic_title"] = 1110000, ["Module:Italic_title2"] = 5200, } 0b605ade3d48ca6718793fd9fac6abbdb09b1f51 Module:Transclusion count/data/S 828 521 1050 2023-07-09T05:13:06Z string2>Ahechtbot 0 [[Wikipedia:BOT|Bot]]: Updated page. Scribunto text/plain return { ["S"] = 3500, ["S-aca"] = 6300, ["S-ach"] = 16000, ["S-aft"] = 217000, ["S-aft/filter"] = 217000, ["S-bef"] = 222000, ["S-bef/filter"] = 222000, ["S-break"] = 5000, ["S-civ"] = 2600, ["S-dip"] = 5300, ["S-end"] = 245000, ["S-gov"] = 7800, ["S-hon"] = 3800, ["S-hou"] = 9500, ["S-inc"] = 13000, ["S-legal"] = 9300, ["S-mil"] = 12000, ["S-new"] = 15000, ["S-non"] = 9300, ["S-npo"] = 4000, ["S-off"] = 40000, ["S-par"] = 50000, ["S-par/en"] = 3200, ["S-par/gb"] = 3300, ["S-par/uk"] = 11000, ["S-par/us-hs"] = 11000, ["S-par/us-sen"] = 2000, ["S-ppo"] = 13000, ["S-prec"] = 3200, ["S-rail"] = 6300, ["S-rail-start"] = 6200, ["S-rail/lines"] = 6300, ["S-reg"] = 20000, ["S-rel"] = 18000, ["S-roy"] = 2700, ["S-s"] = 3600, ["S-sports"] = 10000, ["S-start"] = 239000, ["S-ttl"] = 229000, ["S-vac"] = 5900, ["SCO"] = 3700, ["SDcat"] = 5340000, ["SECOND"] = 2300, ["SGP"] = 2600, ["SIA"] = 2600, ["SIPA"] = 2500, ["SLO"] = 4200, ["SMS"] = 7200, ["SMU"] = 2100, ["SPI_archive_notice"] = 70000, ["SPIarchive_notice"] = 70000, ["SPIcat"] = 3800, ["SPIclose"] = 3300, ["SPIpriorcases"] = 64000, ["SR/Olympics_profile"] = 3200, ["SRB"] = 3600, ["SS"] = 20000, ["SSPa"] = 2600, ["STN"] = 12000, ["SUBJECTSPACE_formatted"] = 42000, ["SUI"] = 8400, ["SVG"] = 3300, ["SVG-Logo"] = 17000, ["SVG-Res"] = 15000, ["SVG-logo"] = 2900, ["SVK"] = 5900, ["SVN"] = 5100, ["SWE"] = 12000, ["Sandbox_other"] = 228000, ["Saturday"] = 2600, ["Saved_book"] = 52000, ["Sc"] = 2800, ["Scholia"] = 2700, ["School_block"] = 13000, ["School_disambiguation"] = 3300, ["Schoolblock"] = 6700, ["Schooldis"] = 2700, ["Schoolip"] = 10000, ["Scientist_icon"] = 15000, ["Scientist_icon2"] = 15000, ["Sclass"] = 31000, ["Sclass2"] = 10000, ["Screen_reader-only"] = 43000, ["Screen_reader-only/styles.css"] = 43000, ["Script"] = 5800, ["Script/Arabic"] = 2100, ["Script/Hebrew"] = 4700, ["Script/Nastaliq"] = 14000, ["Script/doc/id-unk"] = 2900, ["Script/doc/id-unk/core"] = 2900, ["Script/doc/id-unk/is-iso-alpha4"] = 2800, ["Script/doc/id-unk/name-to-alpha4"] = 2900, ["Script/styles.css"] = 3000, ["Script/styles_arabic.css"] = 2100, ["Script/styles_hebrew.css"] = 4700, ["Sdash"] = 3000, ["Search_box"] = 49000, ["Search_link"] = 9600, ["Section_link"] = 51000, ["Section_sizes"] = 2300, ["See"] = 11000, ["See_also"] = 182000, ["Seealso"] = 6700, ["Select_skin"] = 4300, ["Selected_article"] = 2700, ["Selected_picture"] = 2500, ["Self"] = 50000, ["Self-published_inline"] = 3900, ["Self-published_source"] = 6600, ["Self-reference"] = 2700, ["Self-reference_tool"] = 5000, ["Self/migration"] = 34000, ["Self2"] = 2100, ["Sent_off"] = 13000, ["Sentoff"] = 4100, ["Separated_entries"] = 174000, ["Sequence"] = 3700, ["Serial_killer_opentask"] = 3600, ["Series_overview"] = 7600, ["Serif"] = 2800, ["Set_category"] = 35000, ["Set_index_article"] = 5700, ["Sets_taxobox_colour"] = 93000, ["Sfn"] = 151000, ["SfnRef"] = 132000, ["Sfnm"] = 3400, ["Sfnp"] = 17000, ["Sfnref"] = 10000, ["Sfrac"] = 4200, ["Sfrac/styles.css"] = 4200, ["SharedIPEDU"] = 3200, ["Shared_IP"] = 11000, ["Shared_IP_advice"] = 16000, ["Shared_IP_corp"] = 5000, ["Shared_IP_edu"] = 126000, ["Shared_IP_gov"] = 3000, ["Sharedip"] = 3300, ["Sharedipedu"] = 3600, ["Sherdog"] = 2600, ["Ship"] = 85000, ["Ship/maintenancecategory"] = 85000, ["Ship_index"] = 7000, ["Shipboxflag"] = 20000, ["Shipboxflag/core"] = 20000, ["Shipwrecks_navbox_footer"] = 9900, ["Shipwrecks_navbox_footer/link"] = 9900, ["Short_description"] = 5450000, ["Short_description/lowercasecheck"] = 5450000, ["Short_pages_monitor"] = 10000, ["Short_pages_monitor/maximum_length"] = 10000, ["Shortcut"] = 19000, ["Should_be_SVG"] = 9100, ["Show_button"] = 2130000, ["Sic"] = 32000, ["Sica"] = 3000, ["Side_box"] = 1110000, ["Sidebar"] = 252000, ["Sidebar_games_events"] = 36000, ["Sidebar_person"] = 2300, ["Sidebar_person/styles.css"] = 2300, ["Sidebar_with_collapsible_lists"] = 92000, ["Sigfig"] = 3700, ["Significant_figures"] = 5000, ["Significant_figures/rnd"] = 4600, ["Signpost-subscription"] = 2100, ["Sildb_prim"] = 2000, ["Silver02"] = 16000, ["Silver2"] = 47000, ["Silver_medal"] = 5400, ["Similar_names"] = 2100, ["Single+double"] = 6700, ["Single+space"] = 14000, ["Single-innings_cricket_match"] = 3200, ["Single_chart"] = 37000, ["Single_chart/chartnote"] = 37000, ["Single_namespace"] = 199000, ["Singlechart"] = 20000, ["Singles"] = 41000, ["Sister-inline"] = 186000, ["Sister_project"] = 1040000, ["Sister_project_links"] = 10000, ["Sisterlinks"] = 2900, ["Skip_to_talk"] = 12000, ["Skip_to_talk/styles.css"] = 12000, ["Sky"] = 2700, ["Sky/styles.css"] = 2700, ["Slink"] = 12000, ["Small"] = 594000, ["Small_Solar_System_bodies"] = 3600, ["Smallcaps"] = 17000, ["Smallcaps/styles.css"] = 18000, ["Smallcaps_all"] = 2900, ["Smalldiv"] = 21000, ["Smaller"] = 72000, ["Smallsup"] = 21000, ["Smiley"] = 43000, ["Snd"] = 155000, ["Snds"] = 6300, ["Soccer_icon"] = 130000, ["Soccer_icon2"] = 130000, ["Soccer_icon4"] = 5100, ["Soccerbase"] = 13000, ["Soccerbase_season"] = 6700, ["Soccerway"] = 74000, ["Sock"] = 46000, ["Sock_list"] = 3800, ["Sockcat"] = 2000, ["Sockmaster"] = 9300, ["Sockpuppet"] = 236000, ["Sockpuppet/altmaster"] = 2100, ["Sockpuppet/categorise"] = 236000, ["SockpuppetCheckuser"] = 5500, ["Sockpuppet_category"] = 45000, ["Sockpuppet_category/confirmed"] = 23000, ["Sockpuppet_category/suspected"] = 22000, ["Sockpuppetcheckuser"] = 3600, ["Sockpuppeteer"] = 24000, ["Soft_redirect"] = 6100, ["Soft_redirect_protection"] = 8200, ["Softredirect"] = 3200, ["Solar_luminosity"] = 4400, ["Solar_mass"] = 5200, ["Solar_radius"] = 4200, ["Soldier_icon"] = 3900, ["Soldier_icon2"] = 3900, ["Song"] = 8300, ["Songs"] = 19000, ["Songs_category"] = 8300, ["Songs_category/core"] = 8300, ["Sort"] = 115000, ["Sortname"] = 52000, ["Source-attribution"] = 27000, ["Source_check"] = 965000, ["Sourcecheck"] = 965000, ["Sources"] = 2400, ["South_America_topic"] = 2500, ["Sp"] = 250000, ["Space"] = 55000, ["Space+double"] = 18000, ["Space+single"] = 13000, ["Spaced_en_dash"] = 187000, ["Spaced_en_dash_space"] = 6400, ["Spaced_ndash"] = 23000, ["Spaces"] = 2680000, ["Spain_metadata_Wikidata"] = 7500, ["Spamlink"] = 13000, ["Species_Latin_name_abbreviation_disambiguation"] = 2200, ["Species_list"] = 15000, ["Speciesbox"] = 287000, ["Speciesbox/getGenus"] = 287000, ["Speciesbox/getSpecies"] = 287000, ["Speciesbox/name"] = 287000, ["Speciesbox/parameterCheck"] = 287000, ["Speciesbox/trim"] = 287000, ["Specieslist"] = 5300, ["Split_article"] = 3600, ["Spnd"] = 4000, ["Sport_icon"] = 14000, ["Sport_icon2"] = 15000, ["SportsYearCatUSstate"] = 6500, ["SportsYearCatUSstate/core"] = 6500, ["Sports_links"] = 64000, ["Sports_reference"] = 7400, ["Squad_maintenance"] = 3500, ["Sronly"] = 41000, ["Srt"] = 5000, ["Stack"] = 25000, ["Stack/styles.css"] = 34000, ["Stack_begin"] = 8900, ["Stack_end"] = 8900, ["StaleIP"] = 3200, ["Standings_table_end"] = 54000, ["Standings_table_entry"] = 54000, ["Standings_table_entry/record"] = 54000, ["Standings_table_start"] = 54000, ["Standings_table_start/colheader"] = 54000, ["Standings_table_start/colspan"] = 54000, ["Standings_table_start/styles.css"] = 54000, ["Starbox_astrometry"] = 5100, ["Starbox_begin"] = 5300, ["Starbox_catalog"] = 5200, ["Starbox_character"] = 5100, ["Starbox_detail"] = 5000, ["Starbox_end"] = 5200, ["Starbox_image"] = 2900, ["Starbox_observe"] = 5000, ["Starbox_reference"] = 5200, ["Start-Class"] = 64000, ["Start-date"] = 3800, ["Start_and_end_dates"] = 2600, ["Start_box"] = 8100, ["Start_date"] = 431000, ["Start_date_and_age"] = 136000, ["Start_date_and_years_ago"] = 6700, ["Start_of_course_timeline"] = 6000, ["Start_of_course_week"] = 6100, ["Start_tab"] = 4800, ["Startflatlist"] = 143000, ["Static_IP"] = 5900, ["Station"] = 7600, ["Station_link"] = 16000, ["Stdinchicite"] = 10000, ["Steady"] = 13000, ["Stl"] = 14000, ["Stn"] = 7200, ["Stn_art_lnk"] = 2000, ["Stnlnk"] = 30000, ["Storm_colour"] = 5100, ["Storm_path"] = 2100, ["StoryTeleplay"] = 3400, ["Str_endswith"] = 199000, ["Str_find"] = 115000, ["Str_index"] = 13000, ["Str_left"] = 4190000, ["Str_len"] = 18000, ["Str_letter"] = 175000, ["Str_letter/trim"] = 20000, ["Str_number"] = 8000, ["Str_number/trim"] = 169000, ["Str_rep"] = 310000, ["Str_right"] = 3290000, ["Str_trim"] = 5400, ["Str_≠_len"] = 34000, ["Str_≥_len"] = 71000, ["Strfind_short"] = 221000, ["Strikethrough"] = 16000, ["String_split"] = 2400, ["Strip_tags"] = 37000, ["Strong"] = 848000, ["Structurae"] = 2000, ["Stub-Class"] = 33000, ["Stub_Category"] = 13000, ["Stub_category"] = 18000, ["Stub_documentation"] = 36000, ["Student_editor"] = 27000, ["Student_sandbox"] = 4500, ["Student_table_row"] = 5100, ["Students_table"] = 5100, ["Su"] = 9800, ["Su-census1989"] = 4500, ["Sub"] = 4100, ["Subinfobox_bodystyle"] = 35000, ["Subject_bar"] = 18000, ["Suboff"] = 6100, ["Subon"] = 6200, ["Subpage_other"] = 282000, ["Subscription"] = 5800, ["Subscription_required"] = 34000, ["Subsidebar_bodystyle"] = 6700, ["Subst_only"] = 4600, ["Substituted_comment"] = 19000, ["Succession_box"] = 118000, ["Succession_links"] = 162000, ["Summer_Olympics_by_year_category_navigation"] = 2400, ["Summer_Olympics_by_year_category_navigation/core"] = 2400, ["Sunday"] = 2600, ["Sup"] = 58000, ["Superimpose2/base"] = 2100, ["Suppress_categories"] = 5100, ["Surname"] = 66000, ["Swiss_populations"] = 2400, ["Swiss_populations_NC"] = 3000, ["Swiss_populations_YM"] = 2300, ["Swiss_populations_ref"] = 2400, ["Switcher"] = 2500, ["Module:SDcat"] = 5340000, ["Module:SPI_archive_notice"] = 32000, ["Module:Science_redirect"] = 251000, ["Module:Science_redirect/conf"] = 251000, ["Module:Section_link"] = 51000, ["Module:Section_sizes"] = 3400, ["Module:See_also_if_exists"] = 72000, ["Module:Separated_entries"] = 2260000, ["Module:Series_overview"] = 7600, ["Module:Settlement_short_description"] = 709000, ["Module:Shortcut"] = 23000, ["Module:Shortcut/config"] = 23000, ["Module:Shortcut/styles.css"] = 23000, ["Module:Side_box"] = 1140000, ["Module:Side_box/styles.css"] = 1140000, ["Module:Sidebar"] = 334000, ["Module:Sidebar/configuration"] = 334000, ["Module:Sidebar/styles.css"] = 340000, ["Module:Sidebar_games_events"] = 36000, ["Module:Sidebar_games_events/styles.css"] = 36000, ["Module:Singles"] = 41000, ["Module:Sister_project_links"] = 14000, ["Module:Sister_project_links/bar/styles.css"] = 3200, ["Module:Sister_project_links/styles.css"] = 10000, ["Module:Sock_list"] = 3800, ["Module:Sort_title"] = 17000, ["Module:Sortkey"] = 196000, ["Module:Split_article"] = 3600, ["Module:Sports_career"] = 19000, ["Module:Sports_color"] = 68000, ["Module:Sports_color/baseball"] = 34000, ["Module:Sports_color/basketball"] = 22000, ["Module:Sports_color/ice_hockey"] = 3100, ["Module:Sports_rbr_table"] = 11000, ["Module:Sports_rbr_table/styles.css"] = 11000, ["Module:Sports_reference"] = 7400, ["Module:Sports_results"] = 14000, ["Module:Sports_results/styles.css"] = 9400, ["Module:Sports_table"] = 57000, ["Module:Sports_table/WDL"] = 50000, ["Module:Sports_table/WDL_OT"] = 2600, ["Module:Sports_table/WL"] = 3800, ["Module:Sports_table/argcheck"] = 57000, ["Module:Sports_table/styles.css"] = 57000, ["Module:Sports_table/sub"] = 57000, ["Module:Sports_table/totalscheck"] = 41000, ["Module:Stock_tickers/NYSE"] = 2100, ["Module:Storm_categories"] = 5200, ["Module:Storm_categories/categories"] = 5200, ["Module:Storm_categories/colors"] = 5200, ["Module:Storm_categories/icons"] = 5200, ["Module:String"] = 14200000, ["Module:String2"] = 2270000, ["Module:Su"] = 12000, ["Module:Subject_bar"] = 18000, ["Module:Suppress_categories"] = 5200, } 725fcfd17d3d7ee557e68480be6c6d224d27f65b Module:Markup 828 514 1034 2023-07-09T15:59:19Z string2>Gkiyoshinishimoto 0 wikitext text/x-wiki <includeonly>{| style="{{#if:{{{width}}}|width:{{{width}}};}} margin-top:0;margin-left:{{{margin-left|{{{margin|0}}}}}}; border-width:medium; padding:0; {{{style|}}}" {{#if:{{{title|}}} |{{!}}+ '''{{{title}}}'''}} {{#if:{{{noheaders|}}}{{{notitle|}}} | |! scope="col" style="width:50%;{{{colheaderstyle|}}}{{{col1headerstyle|}}}"{{!}} {{{col1|{{{t1|Markup}}}}}} {{!!}}<!-- -->scope="col" style="width:50%;{{{colheaderstyle|}}}{{{col2headerstyle|}}}"{{!}} {{{col2|{{{t2|Renders as}}}}}} }}<!-- -->{{For loop||call=format item|pc1n=template|pc1v=Markup/row|pc2n=c1style|pc2v={{{col1style|{{{codestyle|{{{markupstyle|}}}}}}}}}|pc3n=c2style|pc3v={{{col2style|{{{outputstyle|}}}}}}|pv=item|{{item|c1={{{1|}}}|c2={{{2|}}}}}|{{item|c1={{{3|}}}|c2={{{4|}}}}}|{{item|c1={{{5|}}}|c2={{{6|}}}}}|{{item|c1={{{7|}}}|c2={{{8|}}}}}|{{item|c1={{{9|}}}|c2={{{10|}}}}}|{{item|c1={{{11|}}}|c2={{{12|}}}}}|{{item|c1={{{13|}}}|c2={{{14|}}}}}|{{item|c1={{{15|}}}|c2={{{16|}}}}}|{{item|c1={{{17|}}}|c2={{{18|}}}}}|{{item|c1={{{19|}}}|c2={{{20|}}}}}|{{item|c1={{{21|}}}|c2={{{22|}}}}}|{{item|c1={{{23|}}}|c2={{{24|}}}}}|{{item|c1={{{25|}}}|c2={{{26|}}}}}|{{item|c1={{{27|}}}|c2={{{28|}}}}}|{{item|c1={{{29|}}}|c2={{{30|}}}}}|{{item|c1={{{31|}}}|c2={{{32|}}}}}|{{item|c1={{{33|}}}|c2={{{34|}}}}}|{{item|c1={{{35|}}}|c2={{{36|}}}}}|{{item|c1={{{37|}}}|c2={{{38|}}}}}|{{item|c1={{{39|}}}|c2={{{40|}}}}}}} |- |style="border-width:1px;border-style: none none none;border-color:#ddd; padding:5px; vertical-align:top;"| |style="border-width:1px;border-style: none none none;border-color:#ddd; padding:5px; vertical-align:top;"| |}</includeonly><noinclude> {{Documentation}} </noinclude> deb7b37c29df73fd7da8dd882ed02722686c63a7 Main Page 0 1 1 2023-07-10T07:06:25Z MediaWiki default 1 Create main page wikitext text/x-wiki __NOTOC__ == Welcome to {{SITENAME}}! == This Main Page was created automatically and it seems it hasn't been replaced yet. === For the bureaucrat(s) of this wiki === Hello, and welcome to your new wiki! Thank you for choosing WikiTide for the hosting of your wiki, we hope you will enjoy our hosting. You can immediately start working on your wiki or whenever you want. Need help? No problem! We will help you with your wiki as needed. To start, try checking out these helpful links: * [[mw:Special:MyLanguage/Help:Contents|MediaWiki guide]] (e.g. navigation, editing, deleting pages, blocking users) * [[meta:Special:MyLanguage/FAQ|WikiTide FAQ]] * [[meta:Special:MyLanguage/Request features|Request settings changes on your wiki]]. (Extensions, Skin and Logo/Favicon changes should be done through [[Special:ManageWiki]] on your wiki, see [[meta:Special:MyLanguage/ManageWiki|ManageWiki]] for more information.) ==== I still don't understand X! ==== Well, that's no problem. Even if something isn't explained in the documentation/FAQ, we are still happy to help you. You can find us here: * [[meta:Special:MyLanguage/Help center|On our own WikiTide wiki]] * On [[phorge:|Phorge]] * On [https://wikiforge.net/discord Discord] * On IRC in #wikiforge on irc.libera.chat ([irc://irc.libera.chat/%23wikiforge direct link]; [https://web.libera.chat/?channel=#wikiforge webchat]) === For visitors of this wiki === Hello, the default Main Page of this wiki (this page) has not yet been replaced by the bureaucrat(s) of this wiki. The bureaucrat(s) might still be working on a Main Page, so please check again later! bb47436b9bd8510005d12ff60f515e06d70eb554 4 1 2023-07-10T15:13:39Z BigTa1k 2 /* Welcome to {{SITENAME}}! */ wikitext text/x-wiki __NOTOC__ == Welcome to the {{SITENAME}}! == Under Construction! fc81b4b8463a2121a230258db2367572f276c4a3 Module:Parameter names example 828 492 982 2023-07-10T09:41:44Z infoboxes>Gonnym 0 if these are wanted, they should be handled differently (by passing a parameter to this module) as these cause pages to appear as unknown parameters for templates that don't use them Scribunto text/plain -- This module implements {{parameter names example}}. local p = {} local function makeParam(s) local lb = '&#123;' local rb = '&#125;' return lb:rep(3) .. s .. rb:rep(3) end local function italicize(s) return "''" .. s .. "''" end local function plain(s) return s end function p._main(args, frame) -- Find how we want to format the arguments to the template. local formatFunc if args._display == 'italics' or args._display == 'italic' then formatFunc = italicize elseif args._display == 'plain' then formatFunc = plain else formatFunc = makeParam end -- Build the table of template arguments. local targs = {} for k, v in pairs(args) do if type(k) == 'number' then targs[v] = formatFunc(v) elseif not k:find('^_') then targs[k] = v end end --targs['nocat'] = 'yes'; --targs['categories'] = 'no'; --targs['demo'] = 'yes'; -- Find the template name. local template if args._template then template = args._template else local currentTitle = mw.title.getCurrentTitle() if currentTitle.prefixedText:find('/sandbox$') then template = currentTitle.prefixedText else template = currentTitle.basePageTitle.prefixedText end end -- Call the template with the arguments. frame = frame or mw.getCurrentFrame() local success, result = pcall( frame.expandTemplate, frame, {title = template, args = targs} ) if success then return result else return '' end end function p.main(frame) local args = require('Module:Arguments').getArgs(frame, { wrappers = 'Template:Parameter names example' }) return p._main(args, frame) end return p fdf94fb7a5dc1fabf118d60488a02f1e65b0df24 File:Wikiicon.png 6 2 2 2023-07-10T15:05:42Z BigTa1k 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Template:Infobox 10 304 604 603 2023-07-10T15:38:24Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Infobox]] wikitext text/x-wiki {{#invoke:Infobox|infobox}}<includeonly>{{template other|{{#ifeq:{{PAGENAME}}|Infobox||{{#ifeq:{{str left|{{SUBPAGENAME}}|7}}|Infobox|[[Category:Infobox templates|{{remove first word|{{SUBPAGENAME}}}}]]}}}}|}}</includeonly><noinclude> {{documentation}} <!-- Categories go in the /doc subpage, and interwikis go in Wikidata. --> </noinclude> 817a9f5b6524eced06a57bd1d5fd7179f9369bf2 Template:Tl 10 305 606 605 2023-07-10T15:38:25Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Tl]] wikitext text/x-wiki #REDIRECT [[Template:Template link]] {{Redirect category shell| {{R from move}} }} d6593bb3b4a866249f55d0f34b047a71fe1f1529 Template:Template link 10 306 608 607 2023-07-10T15:38:25Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Template_link]] wikitext text/x-wiki &#123;&#123;[[Template:{{{1}}}|{{{1}}}]]&#125;&#125;<noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> eabbec62efe3044a98ebb3ce9e7d4d43c222351d Template:Yesno 10 307 610 609 2023-07-10T15:38:26Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Yesno]] wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#switch: {{<includeonly>safesubst:</includeonly>lc: {{{1|¬}}} }} |no |n |f |false |off |0 = {{{no|<!-- null -->}}} | = {{{blank|{{{no|<!-- null -->}}}}}} |¬ = {{{¬|}}} |yes |y |t |true |on |1 = {{{yes|yes}}} |#default = {{{def|{{{yes|yes}}}}}} }}<noinclude> {{Documentation}} </noinclude> 629c2937bc5cf7cfe13cd2a598582af832782399 Template:Main other 10 308 612 611 2023-07-10T15:38:27Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Main_other]] wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:0}} | main | other }} }} | main = {{{1|}}} | other | #default = {{{2|}}} }}<noinclude> {{documentation}} <!-- Add categories to the /doc subpage; interwikis go to Wikidata, thank you! --> </noinclude> 86ad907ffeea3cc545159e00cd1f2d6433946450 Template:Nobold 10 309 614 613 2023-07-10T15:38:27Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Nobold]] wikitext text/x-wiki <templatestyles src="Nobold/styles.css"/><span class="nobold">{{{1}}}</span><noinclude> {{documentation}} <!-- PLEASE ADD CATEGORIES AND INTERWIKIS TO THE /doc SUBPAGE, THANKS --> </noinclude> 9c92b5951772bb26ca0fbe9256418b65e47700dd Template:Nobold/styles.css 10 310 616 615 2023-07-10T15:38:28Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Nobold/styles.css]] text text/plain /* {{pp-template}} */ /* Styling for Template:Nobold */ .nobold { font-weight: normal; } 83e5f0adacf8c7984251f1fd9d11ed82ebaadf03 Template:Infobox/styles.css 10 311 618 617 2023-07-10T15:38:29Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Infobox/styles.css]] text text/plain /* {{pp|small=y}} */ /* * This TemplateStyles sheet deliberately does NOT include the full set of * infobox styles. We are still working to migrate all of the manual * infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]] * DO NOT ADD THEM HERE */ /* * not strictly certain these styles are necessary since the modules now * exclusively output infobox-subbox or infobox, not both * just replicating the module faithfully */ .infobox-subbox { padding: 0; border: none; margin: -3px; width: auto; min-width: 100%; font-size: 100%; clear: none; float: none; background-color: transparent; } .infobox-3cols-child { margin: auto; } .infobox .navbar { font-size: 100%; } /* T281642 */ body.skin-minerva .infobox-header, body.skin-minerva .infobox-subheader, body.skin-minerva .infobox-above, body.skin-minerva .infobox-title, body.skin-minerva .infobox-image, body.skin-minerva .infobox-full-data, body.skin-minerva .infobox-below { text-align: center; } e8de6d96f4fde53afc4a6b0fed534405ab59b0a7 Template:Tlx 10 312 620 619 2023-07-10T15:38:30Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Tlx]] wikitext text/x-wiki #REDIRECT [[Template:Template link expanded]] {{Redirect category shell| {{R from move}} }} 1fec988ceb46cb324af228aac45d7cd25fcc9008 Template:Template link expanded 10 313 622 621 2023-07-10T15:38:31Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Template_link_expanded]] wikitext text/x-wiki {{#Invoke:Template link general|main|code=on}}<noinclude> {{Documentation|1=Template:Tlg/doc |content = {{tlg/doc|tlx}} }} <!-- Add categories to the /doc subpage, not here! --> </noinclude> 6c99696fee02f1da368ed20d2504e19bc15b1c13 Template:Template other 10 314 624 623 2023-07-10T15:38:31Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Template_other]] wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:Template}} | template | other }} }} | template = {{{1|}}} | other | #default = {{{2|}}} }}<!--End switch--><noinclude> {{documentation}} <!-- Add categories and interwikis to the /doc subpage, not here! --> </noinclude> 06fb13d264df967b5232141067eb7d2b67372d76 Template:Clear 10 315 626 625 2023-07-10T15:38:32Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Clear]] wikitext text/x-wiki <div style="clear:{{{1|both}}};"></div><noinclude> {{documentation}} </noinclude> 38bab3e3d7fbd3d6800d46556e60bc6bac494d72 Template:Distinguish 10 316 628 627 2023-07-10T15:38:32Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Distinguish]] wikitext text/x-wiki {{#invoke:Distinguish|distinguish}}<noinclude><!-- splitting these lines causes {{Documentation}} template to terminate green shading when Distinguish is used in /doc pages. --> {{Documentation}} <!-- Add categories to the /doc subpage and interwikis to Wikidata, not here! --> </noinclude> f949a4cbfd6eb0ab77b832e69059a40a964b1fd8 Template:Sidebar 10 317 630 629 2023-07-10T15:38:33Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Sidebar]] wikitext text/x-wiki {{#invoke:Sidebar|sidebar}}<noinclude> {{documentation}}</noinclude> ab2498000a99daf324f656b0badd187b4a3e2b42 Template:Hatnote/styles.css 10 318 632 631 2023-07-10T15:38:35Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Hatnote/styles.css]] text text/plain /* {{pp|small=y}} */ .hatnote { font-style: italic; } /* Limit structure CSS to divs because of [[Module:Hatnote inline]] */ div.hatnote { /* @noflip */ padding-left: 1.6em; margin-bottom: 0.5em; } .hatnote i { font-style: normal; } /* The templatestyles element inserts a link element before hatnotes. * TODO: Remove link if/when WMF resolves T200206 */ .hatnote + link + .hatnote { margin-top: -0.5em; } 44680ffd6e888866df2cdfa0341af9c7b97da94c Template:Navbar/styles.css 10 319 634 633 2023-07-10T15:38:36Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Navbar/styles.css]] text text/plain /* {{pp|small=yes}} */ .navbar { display: inline; font-size: 88%; font-weight: normal; } .navbar-collapse { float: left; text-align: left; } .navbar-boxtext { word-spacing: 0; } .navbar ul { display: inline-block; white-space: nowrap; line-height: inherit; } .navbar-brackets::before { margin-right: -0.125em; content: '[ '; } .navbar-brackets::after { margin-left: -0.125em; content: ' ]'; } .navbar li { word-spacing: -0.125em; } .navbar a > span, .navbar a > abbr { text-decoration: inherit; } .navbar-mini abbr { font-variant: small-caps; border-bottom: none; text-decoration: none; cursor: inherit; } .navbar-ct-full { font-size: 114%; margin: 0 7em; } .navbar-ct-mini { font-size: 114%; margin: 0 4em; } 9d4056f949b4f0b159e3d40dfb1a5f01e72f9571 Template:Sidebar/styles.css 10 320 636 635 2023-07-10T15:38:37Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Sidebar/styles.css]] text text/plain /* {{pp-template}} */ /* TODO: Invert width design to be "mobile first" */ .sidebar { /* TODO: Ask if we should have max-width 22em instead */ width: 22em; /* @noflip */ float: right; /* @noflip */ clear: right; /* @noflip */ margin: 0.5em 0 1em 1em; background: #f8f9fa; border: 1px solid #aaa; padding: 0.2em; text-align: center; line-height: 1.4em; font-size: 88%; border-collapse: collapse; /* Timeless has display: none on .nomobile at mobile resolutions, so we * unhide it with display: table and let precedence and proximity win. */ display: table; } /* Unfortunately, so does Minerva desktop, except Minerva drops an * !important on the declaration. So we have to be mean for Minerva users. * Mobile removes the element entirely with `wgMFRemovableClasses` in * https://github.com/wikimedia/operations-mediawiki-config/blob/master/ wmf-config/InitialiseSettings.php#L16992 * which is why displaying it categorically with display: table works. * We don't really want to expose the generic user in the wild on mobile to have * to deal with sidebars. (Maybe the ones with collapsible lists, so that * might be an improvement. That is blocked on [[:phab:T111565]].) */ body.skin-minerva .sidebar { display: table !important; /* also, minerva is way too aggressive about other stylings on tables. * TODO remove when this template gets moved to a div. plans on talk page. * We always float right on Minerva because that's a lot of extra CSS * otherwise. */ float: right !important; margin: 0.5em 0 1em 1em !important; } .sidebar-subgroup { width: 100%; margin: 0; border-spacing: 0; } .sidebar-left { /* @noflip */ float: left; /* @noflip */ clear: left; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-none { float: none; clear: both; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-outer-title { padding: 0 0.4em 0.2em; font-size: 125%; line-height: 1.2em; font-weight: bold; } .sidebar-top-image { padding: 0.4em; } .sidebar-top-caption, .sidebar-pretitle-with-top-image, .sidebar-caption { padding: 0.2em 0.4em 0; line-height: 1.2em; } .sidebar-pretitle { padding: 0.4em 0.4em 0; line-height: 1.2em; } .sidebar-title, .sidebar-title-with-pretitle { padding: 0.2em 0.8em; font-size: 145%; line-height: 1.2em; } .sidebar-title-with-pretitle { padding: 0.1em 0.4em; } .sidebar-image { padding: 0.2em 0.4em 0.4em; } .sidebar-heading { padding: 0.1em 0.4em; } .sidebar-content { padding: 0 0.5em 0.4em; } .sidebar-content-with-subgroup { padding: 0.1em 0.4em 0.2em; } .sidebar-above, .sidebar-below { padding: 0.3em 0.8em; font-weight: bold; } .sidebar-collapse .sidebar-above, .sidebar-collapse .sidebar-below { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; } .sidebar-navbar { text-align: right; font-size: 115%; padding: 0 0.4em 0.4em; } .sidebar-list-title { padding: 0 0.4em; text-align: left; font-weight: bold; line-height: 1.6em; font-size: 105%; } /* centered text with mw-collapsible headers is finicky */ .sidebar-list-title-c { padding: 0 0.4em; text-align: center; margin: 0 3.3em; } @media (max-width: 720px) { /* users have wide latitude to set arbitrary width and margin :( "Super-specific" selector to prevent overriding this appearance by lower level sidebars too */ body.mediawiki .sidebar { width: 100% !important; clear: both; float: none !important; /* Remove when we div based; Minerva is dumb */ margin-left: 0 !important; margin-right: 0 !important; } /* TODO: We might consider making all links wrap at small resolutions and then * only introduce nowrap at higher resolutions. Do when we invert the media * query. */ } 7d621b35a37807a103b59075851fe36201204ceb Template:Div col 10 321 638 637 2023-07-10T15:38:38Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Div_col]] wikitext text/x-wiki <includeonly><templatestyles src="Div col/styles.css"/><!-- --><div class="div-col {{#ifeq:{{{small|}}}|yes|div-col-small}} {{#ifeq:{{{rules|}}}|yes|div-col-rules}} {{{class|}}}" <!-- -->{{#if:{{{colwidth|}}}{{{gap|}}}{{{style|}}}|<!-- -->style="{{#if:{{{colwidth|}}}|column-width: {{{colwidth}}};}}{{#if:{{{gap|}}}|column-gap: {{{gap}}};}}{{#if:{{{style|}}}|{{{style}}}}}"<!-- -->}}><!-- -->{{#if:{{{content|}}}|{{{content}}}</div>}}<!-- Inventory how many pages use small=yes -->{{#ifeq:{{{small|}}}|yes|[[Category:Pages using div col with small parameter]]}}<!-- --></includeonly>{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using div col with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Div col]] with unknown parameter "_VALUE_"; use colwidth= to specify column size |ignoreblank=y | class | colwidth | content | gap | rules | small | style }}<noinclude> {{Documentation}} </noinclude> 6e84133dd867d6c701e7b161878cf66665bb7eb7 Template:Div col/styles.css 10 322 640 639 2023-07-10T15:38:38Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Div_col/styles.css]] text text/plain /* {{pp|small=yes}} */ .div-col { margin-top: 0.3em; column-width: 30em; } .div-col-small { font-size: 90%; } .div-col-rules { column-rule: 1px solid #aaa; } /* Reset top margin for lists in div col */ .div-col dl, .div-col ol, .div-col ul { margin-top: 0; } /* Avoid elements breaking between columns See also Template:No col break */ .div-col li, .div-col dd { page-break-inside: avoid; /* Removed from CSS in favor of break-inside c. 2020 */ break-inside: avoid-column; } /* Unbulleted lists */ .plainlist ol, .plainlist ul { line-height: inherit; list-style: none; margin: 0; } .plainlist ol li, .plainlist ul li { margin-bottom: 0; } 76c2c0a042b9164cff638cd44af5ab129702c141 Template:Div col end 10 323 642 641 2023-07-10T15:38:38Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Div_col_end]] wikitext text/x-wiki <includeonly></div></includeonly><noinclude> {{Documentation|Template:Div col/doc}} </noinclude> 78088d41c21d779e3722f220fcc9773dfbbc1e4f Template:Yesno-no 10 324 644 643 2023-07-10T15:38:39Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Yesno-no]] wikitext text/x-wiki {{safesubst:<noinclude />yesno|{{{1}}}|yes={{{yes|yes}}}|no={{{no|no}}}|blank={{{blank|no}}}|¬={{{¬|no}}}|def={{{def|no}}}}}<noinclude> {{Documentation|Template:Yesno/doc}} <!--Categories go in the doc page referenced above; interwikis go in Wikidata.--> </noinclude> 1ad7b7800da1b867ead8f6ff8cef76e6201b3b56 Template:Para 10 325 646 645 2023-07-10T15:38:39Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Para]] wikitext text/x-wiki <code class="tpl-para" style="word-break:break-word;{{SAFESUBST:<noinclude />#if:{{{plain|}}}|border: none; background-color: inherit;}} {{SAFESUBST:<noinclude />#if:{{{plain|}}}{{{mxt|}}}{{{green|}}}{{{!mxt|}}}{{{red|}}}|color: {{SAFESUBST:<noinclude />#if:{{{mxt|}}}{{{green|}}}|#006400|{{SAFESUBST:<noinclude />#if:{{{!mxt|}}}{{{red|}}}|#8B0000|inherit}}}};}} {{SAFESUBST:<noinclude />#if:{{{style|}}}|{{{style}}}}}">&#124;{{SAFESUBST:<noinclude />#if:{{{1|}}}|{{{1}}}&#61;}}{{{2|}}}</code><noinclude> {{Documentation}} <!--Categories and interwikis go near the bottom of the /doc subpage.--> </noinclude> 06006deea2ed5d552aab61b4332321ab749ae7e8 Template:Documentation 10 326 648 647 2023-07-10T15:38:39Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Documentation]] wikitext text/x-wiki {{#invoke:documentation|main|_content={{ {{#invoke:documentation|contentTitle}}}}}}<noinclude> <!-- Add categories to the /doc subpage --> </noinclude> 9e62b964e96c4e3d478edecbfcb3c0338ae8a276 Template:Documentation/styles.css 10 327 650 649 2023-07-10T15:38:40Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Documentation/styles.css]] text text/plain /* {{pp|small=yes}} */ .documentation, .documentation-metadata { border: 1px solid #a2a9b1; background-color: #ecfcf4; clear: both; } .documentation { margin: 1em 0 0 0; padding: 1em; } .documentation-metadata { margin: 0.2em 0; /* same margin left-right as .documentation */ font-style: italic; padding: 0.4em 1em; /* same padding left-right as .documentation */ } .documentation-startbox { padding-bottom: 3px; border-bottom: 1px solid #aaa; margin-bottom: 1ex; } .documentation-heading { font-weight: bold; font-size: 125%; } .documentation-clear { /* Don't want things to stick out where they shouldn't. */ clear: both; } .documentation-toolbar { font-style: normal; font-size: 85%; } ce0e629c92e3d825ab9fd927fe6cc37d9117b6cb Template:Sandbox other 10 328 652 651 2023-07-10T15:38:40Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Sandbox_other]] wikitext text/x-wiki {{#if:{{#ifeq:{{#invoke:String|sublength|s={{SUBPAGENAME}}|i=0|len=7}}|sandbox|1}}{{#ifeq:{{SUBPAGENAME}}|doc|1}}{{#invoke:String|match|{{PAGENAME}}|/sandbox/styles.css$|plain=false|nomatch=}}|{{{1|}}}|{{{2|}}}}}<!-- --><noinclude>{{documentation}}</noinclude> 91e4ae891d6b791615152c1fbc971414961ba872 Template:Documentation subpage 10 329 654 653 2023-07-10T15:38:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Documentation_subpage]] wikitext text/x-wiki <includeonly><!-- -->{{#ifeq:{{lc:{{SUBPAGENAME}}}} |{{{override|doc}}} | <!--(this template has been transcluded on a /doc or /{{{override}}} page)--> </includeonly><!-- -->{{#ifeq:{{{doc-notice|show}}} |show | {{Mbox | type = notice | style = margin-bottom:1.0em; | image = [[File:Edit-copy green.svg|40px|alt=|link=]] | text = {{strong|This is a [[Wikipedia:Template documentation|documentation]] [[Wikipedia:Subpages|subpage]]}} for {{terminate sentence|{{{1|[[:{{SUBJECTSPACE}}:{{BASEPAGENAME}}]]}}}}}<br />It may contain usage information, [[Wikipedia:Categorization|categories]] and other content that is not part of the original {{#if:{{{text2|}}} |{{{text2}}} |{{#if:{{{text1|}}} |{{{text1}}} |{{#ifeq:{{SUBJECTSPACE}} |{{ns:User}} |{{lc:{{SUBJECTSPACE}}}} template page |{{#if:{{SUBJECTSPACE}} |{{lc:{{SUBJECTSPACE}}}} page|article}}}}}}}}. }} }}<!-- -->{{DEFAULTSORT:{{{defaultsort|{{PAGENAME}}}}}}}<!-- -->{{#if:{{{inhibit|}}} |<!--(don't categorize)--> | <includeonly><!-- -->{{#ifexist:{{NAMESPACE}}:{{BASEPAGENAME}} | [[Category:{{#switch:{{SUBJECTSPACE}} |Template=Template |Module=Module |User=User |#default=Wikipedia}} documentation pages]] | [[Category:Documentation subpages without corresponding pages]] }}<!-- --></includeonly> }}<!-- (completing initial #ifeq: at start of template:) --><includeonly> | <!--(this template has not been transcluded on a /doc or /{{{override}}} page)--> }}<!-- --></includeonly><noinclude>{{Documentation}}</noinclude> 41ca90af0945442788a2dbd08c8c54a61a23c057 Template:Template link with link off 10 330 656 655 2023-07-10T15:38:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Template_link_with_link_off]] wikitext text/x-wiki <includeonly>{{#Invoke:Template link general|main|nowrap=yes|nolink=yes}}</includeonly><noinclude> {{Documentation|1=Template:Tlg/doc |content = {{tlg/doc|tlf}} }} <!-- Add categories to the /doc subpage, not here! --> </noinclude> b099fea5d1f36b0b4b9cb253ad3a9f4e095f6851 Template:Tlf 10 331 658 657 2023-07-10T15:38:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Tlf]] wikitext text/x-wiki #REDIRECT [[Template:Template link with link off]] {{Redirect category shell| {{R from move}} }} 52759e1d3f7c9aa4a03d0b7d4f84f4c6adf53edf Template:High-use 10 332 660 659 2023-07-10T15:38:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:High-use]] wikitext text/x-wiki {{#invoke:High-use|main|1={{{1|}}}|2={{{2|}}}|info={{{info|}}}|demo={{{demo|}}}|form={{{form|}}}|expiry={{{expiry|}}}|system={{{system|}}}}}<noinclude> {{Documentation}} <!-- Add categories to the /doc subpage; interwiki links go to Wikidata, thank you! --> </noinclude> a3322d1bd47ac03df14fa2090855cff4fede9bc7 Template:TemplateData header 10 333 662 661 2023-07-10T15:38:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:TemplateData_header]] wikitext text/x-wiki <div class="templatedata-header">{{#if:{{{noheader|}}}|<!-- noheader: -->{{Template parameter usage|based=y}}|<!-- +header: -->This is the {{#if:{{{nolink|}}}|<!-- +header, nolink TD -->TemplateData|<!-- +header, +link [[TD]]; DEFAULT: -->[[Wikipedia:TemplateData|TemplateData]]}}<!-- e.o. #if:nolink; DEFAULT: --> for this template used by [[mw:Extension:TemplateWizard|TemplateWizard]], [[Wikipedia:VisualEditor|VisualEditor]] and other tools. {{Template parameter usage|based=y}}<!-- e.o. #if:noheader -->}} '''TemplateData for {{{1|{{BASEPAGENAME}}}}}''' </div><includeonly><!-- check parameters -->{{#invoke:Check for unknown parameters|check |unknown={{template other|1=[[Category:Pages using TemplateData header with unknown parameters|_VALUE_]]}} |template=Template:TemplateData header |1 |nolink |noheader |preview=<div class="error" style="font-weight:normal">Unknown parameter '_VALUE_' in [[Template:TemplateData header]].</div> }}<!-- -->{{template other|{{sandbox other|| [[Category:Templates using TemplateData]] }}}}</includeonly><!-- --><noinclude>{{Documentation}}</noinclude> ddfbb4ae793846b96d4c06330417fa6ed4da2adc Template:Template parameter usage 10 334 664 663 2023-07-10T15:38:42Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Template_parameter_usage]] wikitext text/x-wiki {{#switch:{{{label|}}} |=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{#ifeq:{{yesno-no|{{{lc}}}}}|no|C|c}}lick here] to see a monthly parameter usage report for {{#if:{{{1|}}}|[[Template:{{ROOTPAGENAME:{{{1|}}}}}]]|this template}}{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}}. |None|none=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{#ifeq:{{yesno-no|{{{lc}}}}}|no|P|p}}arameter usage report]{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}} |for|For=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{#ifeq:{{yesno-no|{{{lc}}}}}|no|P|p}}arameter usage report] for {{#if:{{{1|}}}|[[Template:{{ROOTPAGENAME:{{{1|}}}}}]]|[[Template:{{ROOTPAGENAME}}]]}}{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}}. |#default=[https://bambots.brucemyers.com/TemplateParam.php?wiki=enwiki&template={{Urlencode:{{#if:{{{1|}}}|{{ROOTPAGENAME:{{{1|}}}}}|{{ROOTPAGENAME}}}}}} {{{label|}}}]{{#ifeq:{{yesno-no|{{{based}}}}}|yes|&#32;based on {{#if:{{{1|}}}|its|this}} TemplateData}} }}<noinclude> {{documentation}} </noinclude> b9cdd1b2e409313904f041c38562a3d6221cc017 Template:Lua 10 335 666 665 2023-07-10T15:38:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Lua]] wikitext text/x-wiki <includeonly>{{#invoke:Lua banner|main}}</includeonly><noinclude> {{Lua|Module:Lua banner}} {{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> dba3962144dacd289dbc34f50fbe0a7bf6d7f2f7 Template:Parameter names example 10 336 668 667 2023-07-10T15:38:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Parameter_names_example]] wikitext text/x-wiki <includeonly>{{#invoke:Parameter names example|main}}</includeonly><noinclude> {{Documentation}} </noinclude> de1e29d6ebc113e9d1649ea6a976625885db8a2f Template:Category link with count 10 337 670 669 2023-07-10T15:38:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Category_link_with_count]] wikitext text/x-wiki [[:Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|<!-- -->{{#if:{{{name|}}}|{{{name}}}|Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}}}<!-- -->]]&nbsp;({{PAGESINCATEGORY:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|{{{2|all}}}}})<noinclude> {{Documentation}} </noinclude> f93f1540b8c157703bd6d24ae35c35bef745981d Template:Infobox/doc 10 338 672 671 2023-07-10T15:38:45Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Infobox/doc]] wikitext text/x-wiki {{Documentation subpage}} <!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) --> {{distinguish|Template:Userbox}} {{#ifeq:{{SUBPAGENAME}}|sandbox||{{High-use}}}} {{Lua|Module:Infobox}} {{Parameter names example |name={{PAGENAME}} <!--|child |subbox |decat--> |title |above |subheader |subheader1 |subheader2={{{subheader2}}}<br/>...... |image|caption |image1|caption1 |image2|caption2={{{caption2}}}<br/>...... |header1=<div style="border-top:1px dashed #ccc;">{{{header1}}}<br/>{{nobold|( ''or'' )}}</div> |label2={{{label1}}} |data2={{{data1}}} |data3=( ''or'' ) |data4=<div style="padding-bottom:0.25em;border-bottom:1px dashed #ccc;">{{{data1}}}</div> |header5={{{header2}}}<br/><div style="padding:0.75em 0 0.5em;">{{nobold|( ''or'' )}}</div> |label6={{{label2}}} |data6={{{data2}}} |data7=( ''or'' ) |data8=<div style="padding-bottom:0.25em;border-bottom:1px dashed #ccc;">{{{data2}}}</div> |data9=<div style="padding:0.75em 0 0.5em;">( ''etc'' )</div> |below }} This template is intended as a meta template: a template used for constructing other templates. '''Note''': In general, it is not meant for use directly in an article, but can be used on a one-off basis if required. [[Help:Infobox]] contains an introduction about the recommended content and design of infoboxes; [[Wikipedia:Manual of Style/Infoboxes]] contains additional style guidelines. See [[WP:List of infoboxes]] and [[:Category:Infobox templates]] for lists of prepared topic-specific infoboxes. == Usage == {{tlf|Infobox}} is a meta-template: used to organise an actual <nowiki>{{Infobox sometopic}}</nowiki> template (like {{tl|Infobox building}}). For <code><nowiki>[[Template:Infobox sometopic]]</nowiki></code>, template code then looks like this, simplified: <syntaxhighlight lang="wikitext"> {{Infobox | name = {{{name|{{PAGENAME}}}}} | image = {{{image|}}} | caption1 = {{{caption|}}} | label1 = Former names | data1 = {{{former_names|}}} | header2 = General information | label3 = Status | data3 = {{{status|}}} ... <!-- etc. --> }} </syntaxhighlight> == Optional control parameters == ; name : If this parameter is present, "view/talk/edit" links will be added to the bottom of the infobox pointing to the named page, prefixed by <code>Template:</code> if no namespace is specified. You may use the value <nowiki>{{subst:PAGENAME}}</nowiki>; however, this is rarely what you want because it will send users clicking these links in an infobox to the template code rather than the data in the infobox they probably want to change. ; child : See the [[#Embedding|Embedding]] section for details. If this is set to "yes", this child infobox should be titled but have no name parameter. This parameter is empty by default, set it to "yes" to activate it. ; subbox : See the [[#Subboxes|Subboxes]] section for details. If this is set to "yes", this subbox should be titled but have no name parameter. This parameter is empty by default, set to "yes" to activate it. It has no effect if the '''child''' parameter is also set to "yes". ; decat : If this is set to "yes", the current page will not be autocategorized in a maintenance category when the generated infobox has some problems or no visible data section. Leave empty by default or set to "yes" to activate it. ; autoheaders: If this is set to any non-blank value, headers which are not followed by data fields are suppressed. See the "[[#Hiding headers when all its data fields are empty|hiding headers when all its data fields are empty]]" section for more details. == Content parameters == === Title === There are two different ways to put a title on an infobox. One contains the title inside the infobox's border in the uppermost cell of the table, the other puts it as a caption on top of the table. You can use them both together, or just one or the other, or neither (though this is not recommended): ; title : Text to put in the caption over the top of the table (or as section header before the whole content of this table, if this is a child infobox). For [[Wikipedia:Manual of Style/Accessibility#Tables|accessibility reasons]], this is the most recommended alternative. ; above : Text to put within the uppermost cell of the table. ; subheader(n) : additional title fields which fit below {{{title}}} and {{{above}}}, but before images. Examples: {{Infobox | name = Infobox/doc | title = Text in caption over infobox | subheader = Subheader of the infobox | header = (the rest of the infobox goes here) }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | title = Text in caption over infobox | subheader = Subheader of the infobox | header = (the rest of the infobox goes here) }} </syntaxhighlight>{{clear}} {{Infobox | name = Infobox/doc | above = Text in uppermost cell of infobox | subheader = Subheader of the infobox | subheader2 = Second subheader of the infobox | header = (the rest of the infobox goes here) }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | above = Text in uppermost cell of infobox | subheader = Subheader of the infobox | subheader2 = Second subheader of the infobox | header = (the rest of the infobox goes here) }} </syntaxhighlight>{{clear}} === Illustration images === ; image(n) : images to display at the top of the template. Use full image syntax, for example <nowiki>[[File:example.png|200px|alt=Example alt text]]</nowiki>. Image is centered by default. See [[WP:ALT]] for more on alt text. ; caption(n) : Text to put underneath the images. === Main data === ; header(n) : Text to use as a header in row n. ; label(n) : Text to use as a label in row n. ; data(n) : Text to display as data in row n. Note: for any given value for (n), not all combinations of parameters are permitted. The presence of a {{para|header''(n)''}} will cause the corresponding {{para|data''(n)''}} (and {{para|rowclass''(n)''}} {{para|label''(n)''}}, see below) to be ignored; the absence of a {{para|data''(n)''}} will cause the corresponding {{para|label''(n)''}} to be ignored. Valid combinations for any single row are: * {{para|class''(n)''}} {{para|header''(n)''}} * {{para|rowclass''(n)''}} {{para|class''(n)''}} {{para|data''(n)''}} * {{para|rowclass''(n)''}} {{para|label''(n)''}} {{para|class''(n)''}} {{para|data''(n)''}} See the rendering of header4, label4, and data4 in the [[#Examples|Examples]] section below. ==== Number ranges ==== To allow flexibility when the layout of an infobox is changed, it may be helpful when developing an infobox to use non-contiguous numbers for header and label/data rows. Parameters for new rows can then be inserted in future without having to renumber existing parameters. For example: <syntaxhighlight lang="wikitext" style="overflow:auto"> | header3 = Section 1 | label5 = Label A | data5 = Data A | label7 = Label C | data7 = Data C | header10 = Section 2 | label12 = Label D | data12 = Data D </syntaxhighlight>{{clear}} It is also possible to automatically renumber parameter names by using [[User:Frietjes/infoboxgap.js]] or [[Module:IncrementParams]]. There is no upper limit on numbers but there must be at most 50 between each used number. ==== Making data fields optional ==== A row with a label but no data is not displayed. This allows for the easy creation of optional infobox content rows. To make a row optional use a parameter that defaults to an empty string, like so: <syntaxhighlight lang="wikitext" style="overflow:auto"> | label5 = Population | data5 = {{{population|}}} </syntaxhighlight>{{clear}} This way if an article doesn't define the population parameter in its infobox the row won't be displayed. For more complex fields with pre-formatted contents that would still be present even if the parameter wasn't set, you can wrap it all in an "#if" statement to make the whole thing vanish when the parameter is not used. For instance, the "#if" statement in the following example reads "#if:the parameter ''mass'' has been supplied |then display it, followed by 'kg'": <syntaxhighlight lang="wikitext" style="overflow:auto"> | label6 = Mass | data6 = {{ #if: {{{mass|}}} | {{{mass}}} kg }} </syntaxhighlight>{{clear}} For more on #if, see [[meta:ParserFunctions##if:|here]]. ==== Hiding headers when all its data fields are empty ==== You can also make headers automatically hide when their section is empty (has no data-row showing). Consider this situation: {{Infobox | title = Example: header with & without data | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} </syntaxhighlight>{{clear}} If you want hide the header when no {{para|data''N''}} values are present, use '''{{para|autoheaders|y}}''': {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} </syntaxhighlight>{{clear}} So, header1 will be shown if any of item1, item2, or item3 is defined. If none of the three parameters are defined the header won't be shown and no empty row appears before the next visible content. Note: if the data has empty css elements, like {{para|data|2=&lt;span style="background:yellow;">&lt;/span>}}, this will be treated as non-empty (having data). If {{para|autoheaders|y}} but there are items that you ''do not'' want to trigger a header, place {{para|headerX|_BLANK_}}. This will serve as an empty header and separate it from the subsequent items. {{Infobox | title = Example: blank header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = _BLANK_ | label6 = label6 text | data6 = Some value, but does not trigger header1 or show header5 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = _BLANK_ | label6 = label6 text | data6 = Some value, but does not trigger header1 or show header5 }} </syntaxhighlight>{{clear}} === Footer === ; below : Text to put in the bottom cell. The bottom cell is intended for footnotes, see-also, and other such information. == Presentation parameters == === Italic titles === Titles of articles with infoboxes may be made italic, in line with [[WP:ITALICTITLE]], by passing the <code>italic title</code> parameter. * Turn on italic titles by passing {{para|italic title|<nowiki>{{{italic title|}}}</nowiki>}} from the infobox. * Turn off by default (notably because only Latin script may be safely rendered in this style and italic may be needed to distinguish foreign language from local English language only in that script, but would be difficult to read for other scripts) but allow some instances to be made italic by passing {{para|italic title|<nowiki>{{{italic title|no}}}</nowiki>}} * Do not make any titles italic by not passing the parameter at all. === CSS styling === {{div col}} ; bodystyle : Applies to the infobox table as a whole ; titlestyle : Applies only to the title caption. Adding a background color is usually inadvisable since the text is rendered "outside" the infobox. ; abovestyle : Applies only to the "above" cell at the top. The default style has font-size:125%; since this cell is usually used for a title, if you want to use the above cell for regular-sized text include "font-size:100%;" in the abovestyle. ; imagestyle : Applies to the cell the image is in. This includes the text of the image caption, but you should set text properties with captionstyle instead of imagestyle in case the caption is moved out of this cell in the future. ; captionstyle : Applies to the text of the image caption. ; rowstyle(n) : This parameter is inserted into the <code>style</code> attribute for the specified row. ; headerstyle : Applies to all header cells ; subheaderstyle : Applies to all subheader cells ; labelstyle : Applies to all label cells ; datastyle : Applies to all data cells ; belowstyle : Applies only to the below cell {{div col end}} === HTML classes and microformats === {{div col}} ; bodyclass : This parameter is inserted into the <code>class</code> attribute for the infobox as a whole. ; titleclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''title''' caption. <!-- currently not implemented in Lua module ; aboverowclass : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''above''' cell is on. --> ; aboveclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''above''' cell. ; subheaderrowclass(n) : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''subheader''' is on. ; subheaderclass(n) : This parameter is inserted into the <code>class</code> attribute for the infobox's '''subheader'''. ; imagerowclass(n) : These parameters are inserted into the <code>class</code> attribute for the complete table row their respective '''image''' is on. ; imageclass : This parameter is inserted into the <code>class</code> attribute for the '''image'''. ; rowclass(n) : This parameter is inserted into the <code>class</code> attribute for the specified row including the '''label''' and '''data''' cells. ; class(n) : This parameter is inserted into the <code>class</code> attribute for the '''data''' cell of the specified row. If there's no '''data''' cell it has no effect. <!-- currently not implemented in Lua module ; belowrowclass : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''below''' cell is on. --> ; belowclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''below''' cell. {{div col end}} This template supports the addition of microformat information. This is done by adding "class" attributes to various data cells, indicating what kind of information is contained within. Multiple class names may be specified, separated by spaces, some of them being used as selectors for custom styling according to a project policy or to the skin selected in user preferences, others being used for microformats. To flag an infobox as containing [[hCard]] information, for example, add the following parameter: <syntaxhighlight lang="wikitext" style="overflow:auto"> | bodyclass = vcard </syntaxhighlight>{{clear}} And for each row containing a data cell that's part of the vcard, add a corresponding class parameter: <syntaxhighlight lang="wikitext" style="overflow:auto"> | class1 = fn | class2 = org | class3 = tel </syntaxhighlight>{{clear}} ...and so forth. "above" and "title" can also be given classes, since these are usually used to display the name of the subject of the infobox. See [[Wikipedia:WikiProject Microformats]] for more information on adding microformat information to Wikipedia, and [[microformat]] for more information on microformats in general. == Examples == Notice how the row doesn't appear in the displayed infobox when a '''label''' is defined without an accompanying '''data''' cell, and how all of them are displayed when a '''header''' is defined on the same row as a '''data''' cell. Also notice that '''subheaders''' are not bold by default like the '''headers''' used to split the main data section, because this role is meant to be for the '''above''' cell : {{Infobox |name = Infobox/doc |bodystyle = |titlestyle = |abovestyle = background:#cfc; |subheaderstyle = |title = Test Infobox |above = Above text |subheader = Subheader above image |subheader2 = Second subheader |imagestyle = |captionstyle = |image = [[File:Example-serious.jpg|200px|alt=Example alt text]] |caption = Caption displayed below File:Example-serious.jpg |headerstyle = background:#ccf; |labelstyle = background:#ddf; |datastyle = |header1 = Header defined alone | label1 = | data1 = |header2 = | label2 = Label defined alone does not display (needs data, or is suppressed) | data2 = |header3 = | label3 = | data3 = Data defined alone |header4 = All three defined (header, label, data, all with same number) | label4 = does not display (same number as a header) | data4 = does not display (same number as a header) |header5 = | label5 = Label and data defined (label) | data5 = Label and data defined (data) |belowstyle = background:#ddf; |below = Below text }} <syntaxhighlight lang="wikitext"> {{Infobox |name = Infobox/doc |bodystyle = |titlestyle = |abovestyle = background:#cfc; |subheaderstyle = |title = Test Infobox |above = Above text |subheader = Subheader above image |subheader2 = Second subheader |imagestyle = |captionstyle = |image = [[File:Example-serious.jpg|200px|alt=Example alt text]] |caption = Caption displayed below File:Example-serious.jpg |headerstyle = background:#ccf; |labelstyle = background:#ddf; |datastyle = |header1 = Header defined alone | label1 = | data1 = |header2 = | label2 = Label defined alone does not display (needs data, or is suppressed) | data2 = |header3 = | label3 = | data3 = Data defined alone |header4 = All three defined (header, label, data, all with same number) | label4 = does not display (same number as a header) | data4 = does not display (same number as a header) |header5 = | label5 = Label and data defined (label) | data5 = Label and data defined (data) |belowstyle = background:#ddf; |below = Below text }} </syntaxhighlight> For this example, the {{para|bodystyle}} and {{para|labelstyle}} parameters are used to adjust the infobox width and define a default width for the column of labels: {{Infobox |name = Infobox/doc |bodystyle = width:20em |titlestyle = |title = Test Infobox |headerstyle = |labelstyle = width:33% |datastyle = |header1 = | label1 = Label 1 | data1 = Data 1 |header2 = | label2 = Label 2 | data2 = Data 2 |header3 = | label3 = Label 3 | data3 = Data 3 |header4 = Header 4 | label4 = | data4 = |header5 = | label5 = Label 5 | data5 = Data 5: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |belowstyle = |below = Below text }} <syntaxhighlight lang="wikitext"> {{Infobox |name = Infobox/doc |bodystyle = width:20em |titlestyle = |title = Test Infobox |headerstyle = |labelstyle = width:33% |datastyle = |header1 = | label1 = Label 1 | data1 = Data 1 |header2 = | label2 = Label 2 | data2 = Data 2 |header3 = | label3 = Label 3 | data3 = Data 3 |header4 = Header 4 | label4 = | data4 = |header5 = | label5 = Label 5 | data5 = Data 5: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |belowstyle = |below = Below text }} </syntaxhighlight> == Embedding == <!--Linked from [[Template:Subinfobox bodystyle/doc]]--> One infobox template can be embedded into another using the {{para|child}} parameter. This feature can be used to create a modular infobox, or to create better-defined logical sections. Long ago, it was necessary to use embedding in order to create infoboxes with more than 99 rows; but nowadays there's no limit to the number of rows that can be defined in a single instance of <code><nowiki>{{infobox}}</nowiki></code>. {{Infobox | title = Top level title | data1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | data2 = {{Infobox | decat = yes | child = yes |title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | data1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | data2 = {{Infobox | decat = yes | child = yes |title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} Note, in the examples above, the child infobox is placed in a <code>data</code> field, not a <code>header</code> field. Notice that the section subheadings are not in bold font if bolding is not explicitly specified. To obtain bold section headings, place the child infobox in a '''header''' field (but not in a '''label''' field because it would not be displayed!), either using {{Infobox | title = Top level title | header1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | header2 = {{Infobox | decat = yes | child = yes | title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | header1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | header2 = {{Infobox | decat = yes | child = yes | title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} or, {{Infobox | title = Top level title | header1 = First subsection {{Infobox | decat = yes | child = yes | label1 = Label 1.1 | data1 = Data 1.1 }} | header2 = Second subsection {{Infobox | decat = yes | child = yes | label1 = Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | header1 = First subsection {{Infobox | decat = yes | child = yes | label1 = Label 1.1 | data1 = Data 1.1 }} | header2 = Second subsection {{Infobox | decat = yes | child = yes | label1 = Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} Note that omitting the {{para|title}} parameter, and not including any text preceding the embedded infobox, may result in spurious blank table rows, creating gaps in the visual presentation. The garbage output can be suppressed using {{para|rowstyleN|display: none}}, replacing N with the data/header number. [[Wikipedia:WikiProject Infoboxes/embed]] includes some links to Wikipedia articles which include infoboxes embedded within other infoboxes. == Subboxes == An alternative method for embedding is to use {{para|subbox|yes}}, which removes the outer border from the infobox, but preserves the interior structure. One feature of this approach is that the parent and child boxes need not have the same structure, and the label and data fields are not aligned between the parent and child boxes because they are not in the same parent table. {{Infobox | headerstyle = background-color:#eee; | labelstyle = background-color:#eee; | header1 = Main 1 | header2 = Main 2 | data3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | data4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | label5 = Label 5 | data5 = Data 5 | header6 = Main 6 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | headerstyle = background-color:#eee; | labelstyle = background-color:#eee; | header1 = Main 1 | header2 = Main 2 | data3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | data4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | label5 = Label 5 | data5 = Data 5 | header6 = Main 6 }} </syntaxhighlight>{{clear}} Similar embedding techniques may be used within content parameters of some other templates generating tables (such as [[:Template:Sidebar|Sidebar]]) : {{Sidebar | navbar = off | headingstyle = background-color:#eee; | heading1 = Heading 1 | heading2 = Heading 2 | content3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | content4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | heading5 = Heading 5 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Sidebar | navbar = off | headingstyle = background-color:#eee; | heading1 = Heading 1 | heading2 = Heading 2 | content3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | content4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | heading5 = Heading 5 }} </syntaxhighlight>{{clear}} Note that the default padding of the parent data cell containing each subbox is still visible, so the subboxes are slightly narrower than the parent box and there's a higher vertical spacing between standard cells of the parent box than between cells of distinct subboxes. == Controlling line-breaking in embedded bulletless lists == Template {{tlx|nbsp}} may be used with {{tlx|wbr}} and {{tlx|nowrap}} to control line-breaking in bulletless lists embedded in infoboxes (e.g. cast list in {{tlx|Infobox film}}), to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details. == Full blank syntax == (Note: there is no limit to the number of possible rows; only 20 are given below since infoboxes larger than that will be relatively rare. Just extend the numbering as needed. The microformat "class" parameters are also omitted as they are not commonly used.) <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | child = {{{child|}}} | subbox = {{{subbox|}}} | italic title = {{{italic title|no}}} | templatestyles = | child templatestyles = | grandchild templatestyles = | bodystyle = | titlestyle = | abovestyle = | subheaderstyle = | title = | above = | subheader = | imagestyle = | captionstyle = | image = | caption = | image2 = | caption2 = | headerstyle = | labelstyle = | datastyle = | header1 = | label1 = | data1 = | header2 = | label2 = | data2 = | header3 = | label3 = | data3 = | header4 = | label4 = | data4 = | header5 = | label5 = | data5 = | header6 = | label6 = | data6 = | header7 = | label7 = | data7 = | header8 = | label8 = | data8 = | header9 = | label9 = | data9 = | header10 = | label10 = | data10 = | header11 = | label11 = | data11 = | header12 = | label12 = | data12 = | header13 = | label13 = | data13 = | header14 = | label14 = | data14 = | header15 = | label15 = | data15 = | header16 = | label16 = | data16 = | header17 = | label17 = | data17 = | header18 = | label18 = | data18 = | header19 = | label19 = | data19 = | header20 = | label20 = | data20 = | belowstyle = | below = }} </syntaxhighlight>{{clear}} {{Help:Infobox/user style}} == Porting to other MediaWikis == The infobox template requires the [[:mw:Extension:Scribunto|Scribunto]] extension. [[Wikipedia:WikiProject Transwiki|WikiProject Transwiki]] has a version of this template that has been modified to work on other MediaWikis. == TemplateData == {{TemplateData header}} <templatedata> { "description": "This template is intended as a meta template, a template used for constructing other templates. In general, it is not meant for use directly in an article but can be used on a one-off basis if required.", "format": "{{_\n| ________________ = _\n}}\n", "params": { "title": { "label": "Title", "description": "Title displayed above the infobox", "type": "string", "suggested": true }, "image": { "label": "Image", "description": "Image illustrating the topic. Use full image syntax.", "type": "content", "suggested": true, "example": "[[File:example.png|200px|alt=Example alt text]]" }, "caption": { "label": "Caption", "description": "caption for the image", "type": "content", "suggested": true } }, "paramOrder": [ "title", "image", "caption" ] } </templatedata> ==Tracking categories== * {{Category link with count|Articles with missing Wikidata information}} * {{Category link with count|Articles using infobox templates with no data rows}} * {{Category link with count|Pages using embedded infobox templates with the title parameter}} ==See also== * [[Module:Infobox]], the [[WP:LUA|Lua]] module on which this template is based * [[Module:Check for unknown parameters]] * {{tl|Infobox3cols}} * {{tl|Navbox}} and {{tl|Sidebar}} * [[Wikipedia:List of infoboxes|List of infoboxes]] * [[:Module:InfoboxImage]] <includeonly>{{Sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:Infobox templates| ]] [[Category:Wikipedia metatemplates|Infobox]] [[Category:Templates generating microformats]] [[Category:Templates that add a tracking category]] [[Category:Templates based on the Infobox Lua module]] }}</includeonly> 7b5cc59c733eab17e47789808768a3f1064804b1 Template:Infobox/user style 10 339 674 673 2023-07-10T15:38:45Z BigTa1k 2 1 revision imported from [[:wikipedia:Help:Infobox/user_style]] wikitext text/x-wiki {{{heading| ==Infoboxes and user style == }}} Users can have [[WP:User style|user CSS]] that hides<!--, moves, or makes collapsible--> any infoboxes in their own browsers. To hide all infoboxes, add the following to [[Special:MyPage/common.css]] (for all [[WP:Skin|skins]], or [[Special:MyPage/skin.css]] for just the current skin), on a line by itself: <syntaxhighlight lang="css">div.mw-parser-output .infobox { display: none; }</syntaxhighlight> Alternatively, you can add the following code to [[Special:MyPage/common.js|your common.js]] or into a browser user script that is executed by an extension like [[Greasemonkey]]: <syntaxhighlight lang="js">$('.infobox').hide();</syntaxhighlight> Be aware that although{{#if:{{{guideline|}}}||, per [[WP:Manual of Style/Infoboxes]],}} all information in an infobox ideally should also be found in the main body of an article, there isn't perfect compliance with this guideline. For example, the full taxonomic hierarchy in {{tlx|Taxobox}}, and the OMIM and other medical database codes of {{tlx|Infobox disease}} are often not found in the main article content. The infobox is also often the location of the most significant, even only, image in an article. There is a userscript which removes infoboxes but moves the images contained to separate thumbnails: [[User:Maddy from Celeste/disinfobox.js]].<!-- Needs Special:Mypage/common.js options for: * Making infoboxes collapsible ** Making them auto-collapsed * Moving infoboxes to bottom of page --><noinclude> {{Documentation|content= This documentation snippet is transcluded at [[Help:Infobox]], [[Template:Infobox/doc]], [[WP:Customisation#Hiding specific messages]], [[Help:User style]], [[WP:Manual of Style/Infoboxes]], and other places where this information is relevant. As a template, this snippet takes a {{para|heading}} parameter to replace the level-2 <code>==Infoboxes and user style==</code> section heading code, as needed. E.g., for a <code>=== ... ===</code> level-3 heading: <code><nowiki>heading={{=}}{{=}}{{=}}Infoboxes and user style{{=}}{{=}}{{=}}</nowiki></code> }} </noinclude> ba4dac68eb2bdc49a32f2a11b9afd52381bf06b5 Template:Message box/ombox.css 10 340 676 675 2023-07-10T15:38:45Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Message_box/ombox.css]] text text/plain /* {{pp|small=y}} */ .ombox { margin: 4px 0; border-collapse: collapse; border: 1px solid #a2a9b1; /* Default "notice" gray */ background-color: #f8f9fa; box-sizing: border-box; } /* For the "small=yes" option. */ .ombox.mbox-small { font-size: 88%; line-height: 1.25em; } .ombox-speedy { border: 2px solid #b32424; /* Red */ background-color: #fee7e6; /* Pink */ } .ombox-delete { border: 2px solid #b32424; /* Red */ } .ombox-content { border: 1px solid #f28500; /* Orange */ } .ombox-style { border: 1px solid #fc3; /* Yellow */ } .ombox-move { border: 1px solid #9932cc; /* Purple */ } .ombox-protection { border: 2px solid #a2a9b1; /* Gray-gold */ } .ombox .mbox-text { border: none; /* @noflip */ padding: 0.25em 0.9em; width: 100%; } .ombox .mbox-image { border: none; /* @noflip */ padding: 2px 0 2px 0.9em; text-align: center; } .ombox .mbox-imageright { border: none; /* @noflip */ padding: 2px 0.9em 2px 0; text-align: center; } /* An empty narrow cell */ .ombox .mbox-empty-cell { border: none; padding: 0; width: 1px; } .ombox .mbox-invalid-type { text-align: center; } @media (min-width: 720px) { .ombox { margin: 4px 10%; } .ombox.mbox-small { /* @noflip */ clear: right; /* @noflip */ float: right; /* @noflip */ margin: 4px 0 4px 1em; width: 238px; } } 8fe3df4bb607e699eab2dbd23bd4a1a446391002 Template:Hlist/styles.css 10 341 678 677 2023-07-10T15:38:46Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Hlist/styles.css]] text text/plain /* {{pp-protected|reason=match parent|small=yes}} */ /* * hlist styles are defined in core and Minerva and differ in Minerva. The * current definitions here (2023-01-01) are sufficient to override Minerva * without use of the hlist-separated class. The most problematic styles were * related to margin, padding, and the bullet. Check files listed at * [[MediaWiki talk:Common.css/to do#hlist-separated]] */ /* * TODO: When the majority of readership supports it (or some beautiful world * in which grade C support is above the minimum threshold), use :is() */ .hlist dl, .hlist ol, .hlist ul { margin: 0; padding: 0; } /* Display list items inline */ .hlist dd, .hlist dt, .hlist li { /* * don't trust the note that says margin doesn't work with inline * removing margin: 0 makes dds have margins again * We also want to reset margin-right in Minerva */ margin: 0; display: inline; } /* Display requested top-level lists inline */ .hlist.inline, .hlist.inline dl, .hlist.inline ol, .hlist.inline ul, /* Display nested lists inline */ .hlist dl dl, .hlist dl ol, .hlist dl ul, .hlist ol dl, .hlist ol ol, .hlist ol ul, .hlist ul dl, .hlist ul ol, .hlist ul ul { display: inline; } /* Hide empty list items */ .hlist .mw-empty-li { display: none; } /* TODO: :not() can maybe be used here to remove the later rule. naive test * seems to work. more testing needed. like so: *.hlist dt:not(:last-child)::after { * content: ": "; *} *.hlist dd:not(:last-child)::after, *.hlist li:not(:last-child)::after { * content: " · "; * font-weight: bold; *} */ /* Generate interpuncts */ .hlist dt::after { content: ": "; } .hlist dd::after, .hlist li::after { content: " · "; font-weight: bold; } .hlist dd:last-child::after, .hlist dt:last-child::after, .hlist li:last-child::after { content: none; } /* Add parentheses around nested lists */ .hlist dd dd:first-child::before, .hlist dd dt:first-child::before, .hlist dd li:first-child::before, .hlist dt dd:first-child::before, .hlist dt dt:first-child::before, .hlist dt li:first-child::before, .hlist li dd:first-child::before, .hlist li dt:first-child::before, .hlist li li:first-child::before { content: " ("; font-weight: normal; } .hlist dd dd:last-child::after, .hlist dd dt:last-child::after, .hlist dd li:last-child::after, .hlist dt dd:last-child::after, .hlist dt dt:last-child::after, .hlist dt li:last-child::after, .hlist li dd:last-child::after, .hlist li dt:last-child::after, .hlist li li:last-child::after { content: ")"; font-weight: normal; } /* Put ordinals in front of ordered list items */ .hlist ol { counter-reset: listitem; } .hlist ol > li { counter-increment: listitem; } .hlist ol > li::before { content: " " counter(listitem) "\a0"; } .hlist dd ol > li:first-child::before, .hlist dt ol > li:first-child::before, .hlist li ol > li:first-child::before { content: " (" counter(listitem) "\a0"; } 8c9dd9c9c00f30eead17fe10f51d183333e81f33 Module:Documentation 828 344 683 682 2023-07-10T15:44:36Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Documentation]] Scribunto text/plain -- This module implements {{documentation}}. -- Get required modules. local getArgs = require('Module:Arguments').getArgs -- Get the config table. local cfg = mw.loadData('Module:Documentation/config') local p = {} -- Often-used functions. local ugsub = mw.ustring.gsub ---------------------------------------------------------------------------- -- Helper functions -- -- These are defined as local functions, but are made available in the p -- table for testing purposes. ---------------------------------------------------------------------------- local function message(cfgKey, valArray, expectType) --[[ -- Gets a message from the cfg table and formats it if appropriate. -- The function raises an error if the value from the cfg table is not -- of the type expectType. The default type for expectType is 'string'. -- If the table valArray is present, strings such as $1, $2 etc. in the -- message are substituted with values from the table keys [1], [2] etc. -- For example, if the message "foo-message" had the value 'Foo $2 bar $1.', -- message('foo-message', {'baz', 'qux'}) would return "Foo qux bar baz." --]] local msg = cfg[cfgKey] expectType = expectType or 'string' if type(msg) ~= expectType then error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2) end if not valArray then return msg end local function getMessageVal(match) match = tonumber(match) return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4) end return ugsub(msg, '$([1-9][0-9]*)', getMessageVal) end p.message = message local function makeWikilink(page, display) if display then return mw.ustring.format('[[%s|%s]]', page, display) else return mw.ustring.format('[[%s]]', page) end end p.makeWikilink = makeWikilink local function makeCategoryLink(cat, sort) local catns = mw.site.namespaces[14].name return makeWikilink(catns .. ':' .. cat, sort) end p.makeCategoryLink = makeCategoryLink local function makeUrlLink(url, display) return mw.ustring.format('[%s %s]', url, display) end p.makeUrlLink = makeUrlLink local function makeToolbar(...) local ret = {} local lim = select('#', ...) if lim < 1 then return nil end for i = 1, lim do ret[#ret + 1] = select(i, ...) end -- 'documentation-toolbar' return '<span class="' .. message('toolbar-class') .. '">(' .. table.concat(ret, ' &#124; ') .. ')</span>' end p.makeToolbar = makeToolbar ---------------------------------------------------------------------------- -- Argument processing ---------------------------------------------------------------------------- local function makeInvokeFunc(funcName) return function (frame) local args = getArgs(frame, { valueFunc = function (key, value) if type(value) == 'string' then value = value:match('^%s*(.-)%s*$') -- Remove whitespace. if key == 'heading' or value ~= '' then return value else return nil end else return value end end }) return p[funcName](args) end end ---------------------------------------------------------------------------- -- Entry points ---------------------------------------------------------------------------- function p.nonexistent(frame) if mw.title.getCurrentTitle().subpageText == 'testcases' then return frame:expandTemplate{title = 'module test cases notice'} else return p.main(frame) end end p.main = makeInvokeFunc('_main') function p._main(args) --[[ -- This function defines logic flow for the module. -- @args - table of arguments passed by the user --]] local env = p.getEnvironment(args) local root = mw.html.create() root :wikitext(p._getModuleWikitext(args, env)) :wikitext(p.protectionTemplate(env)) :wikitext(p.sandboxNotice(args, env)) :tag('div') -- 'documentation-container' :addClass(message('container')) :attr('role', 'complementary') :attr('aria-labelledby', args.heading ~= '' and 'documentation-heading' or nil) :attr('aria-label', args.heading == '' and 'Documentation' or nil) :newline() :tag('div') -- 'documentation' :addClass(message('main-div-classes')) :newline() :wikitext(p._startBox(args, env)) :wikitext(p._content(args, env)) :tag('div') -- 'documentation-clear' :addClass(message('clear')) :done() :newline() :done() :wikitext(p._endBox(args, env)) :done() :wikitext(p.addTrackingCategories(env)) -- 'Module:Documentation/styles.css' return mw.getCurrentFrame():extensionTag ( 'templatestyles', '', {src=cfg['templatestyles'] }) .. tostring(root) end ---------------------------------------------------------------------------- -- Environment settings ---------------------------------------------------------------------------- function p.getEnvironment(args) --[[ -- Returns a table with information about the environment, including title -- objects and other namespace- or path-related data. -- @args - table of arguments passed by the user -- -- Title objects include: -- env.title - the page we are making documentation for (usually the current title) -- env.templateTitle - the template (or module, file, etc.) -- env.docTitle - the /doc subpage. -- env.sandboxTitle - the /sandbox subpage. -- env.testcasesTitle - the /testcases subpage. -- -- Data includes: -- env.protectionLevels - the protection levels table of the title object. -- env.subjectSpace - the number of the title's subject namespace. -- env.docSpace - the number of the namespace the title puts its documentation in. -- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace. -- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template. -- -- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value -- returned will be nil. --]] local env, envFuncs = {}, {} -- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value -- returned by that function is memoized in the env table so that we don't call any of the functions -- more than once. (Nils won't be memoized.) setmetatable(env, { __index = function (t, key) local envFunc = envFuncs[key] if envFunc then local success, val = pcall(envFunc) if success then env[key] = val -- Memoise the value. return val end end return nil end }) function envFuncs.title() -- The title object for the current page, or a test page passed with args.page. local title local titleArg = args.page if titleArg then title = mw.title.new(titleArg) else title = mw.title.getCurrentTitle() end return title end function envFuncs.templateTitle() --[[ -- The template (or module, etc.) title object. -- Messages: -- 'sandbox-subpage' --> 'sandbox' -- 'testcases-subpage' --> 'testcases' --]] local subjectSpace = env.subjectSpace local title = env.title local subpage = title.subpageText if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then return mw.title.makeTitle(subjectSpace, title.baseText) else return mw.title.makeTitle(subjectSpace, title.text) end end function envFuncs.docTitle() --[[ -- Title object of the /doc subpage. -- Messages: -- 'doc-subpage' --> 'doc' --]] local title = env.title local docname = args[1] -- User-specified doc page. local docpage if docname then docpage = docname else docpage = env.docpageBase .. '/' .. message('doc-subpage') end return mw.title.new(docpage) end function envFuncs.sandboxTitle() --[[ -- Title object for the /sandbox subpage. -- Messages: -- 'sandbox-subpage' --> 'sandbox' --]] return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage')) end function envFuncs.testcasesTitle() --[[ -- Title object for the /testcases subpage. -- Messages: -- 'testcases-subpage' --> 'testcases' --]] return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage')) end function envFuncs.protectionLevels() -- The protection levels table of the title object. return env.title.protectionLevels end function envFuncs.subjectSpace() -- The subject namespace number. return mw.site.namespaces[env.title.namespace].subject.id end function envFuncs.docSpace() -- The documentation namespace number. For most namespaces this is the -- same as the subject namespace. However, pages in the Article, File, -- MediaWiki or Category namespaces must have their /doc, /sandbox and -- /testcases pages in talk space. local subjectSpace = env.subjectSpace if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then return subjectSpace + 1 else return subjectSpace end end function envFuncs.docpageBase() -- The base page of the /doc, /sandbox, and /testcases subpages. -- For some namespaces this is the talk page, rather than the template page. local templateTitle = env.templateTitle local docSpace = env.docSpace local docSpaceText = mw.site.namespaces[docSpace].name -- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon. return docSpaceText .. ':' .. templateTitle.text end function envFuncs.compareUrl() -- Diff link between the sandbox and the main template using [[Special:ComparePages]]. local templateTitle = env.templateTitle local sandboxTitle = env.sandboxTitle if templateTitle.exists and sandboxTitle.exists then local compareUrl = mw.uri.fullUrl( 'Special:ComparePages', { page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText} ) return tostring(compareUrl) else return nil end end return env end ---------------------------------------------------------------------------- -- Auxiliary templates ---------------------------------------------------------------------------- p.getModuleWikitext = makeInvokeFunc('_getModuleWikitext') function p._getModuleWikitext(args, env) local currentTitle = mw.title.getCurrentTitle() if currentTitle.contentModel ~= 'Scribunto' then return end pcall(require, currentTitle.prefixedText) -- if it fails, we don't care local moduleWikitext = package.loaded["Module:Module wikitext"] if moduleWikitext then return moduleWikitext.main() end end function p.sandboxNotice(args, env) --[=[ -- Generates a sandbox notice for display above sandbox pages. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'sandbox-notice-image' --> '[[File:Sandbox.svg|50px|alt=|link=]]' -- 'sandbox-notice-blurb' --> 'This is the $1 for $2.' -- 'sandbox-notice-diff-blurb' --> 'This is the $1 for $2 ($3).' -- 'sandbox-notice-pagetype-template' --> '[[Wikipedia:Template test cases|template sandbox]] page' -- 'sandbox-notice-pagetype-module' --> '[[Wikipedia:Template test cases|module sandbox]] page' -- 'sandbox-notice-pagetype-other' --> 'sandbox page' -- 'sandbox-notice-compare-link-display' --> 'diff' -- 'sandbox-notice-testcases-blurb' --> 'See also the companion subpage for $1.' -- 'sandbox-notice-testcases-link-display' --> 'test cases' -- 'sandbox-category' --> 'Template sandboxes' --]=] local title = env.title local sandboxTitle = env.sandboxTitle local templateTitle = env.templateTitle local subjectSpace = env.subjectSpace if not (subjectSpace and title and sandboxTitle and templateTitle and mw.title.equals(title, sandboxTitle)) then return nil end -- Build the table of arguments to pass to {{ombox}}. We need just two fields, "image" and "text". local omargs = {} omargs.image = message('sandbox-notice-image') -- Get the text. We start with the opening blurb, which is something like -- "This is the template sandbox for [[Template:Foo]] (diff)." local text = '' local pagetype if subjectSpace == 10 then pagetype = message('sandbox-notice-pagetype-template') elseif subjectSpace == 828 then pagetype = message('sandbox-notice-pagetype-module') else pagetype = message('sandbox-notice-pagetype-other') end local templateLink = makeWikilink(templateTitle.prefixedText) local compareUrl = env.compareUrl if compareUrl then local compareDisplay = message('sandbox-notice-compare-link-display') local compareLink = makeUrlLink(compareUrl, compareDisplay) text = text .. message('sandbox-notice-diff-blurb', {pagetype, templateLink, compareLink}) else text = text .. message('sandbox-notice-blurb', {pagetype, templateLink}) end -- Get the test cases page blurb if the page exists. This is something like -- "See also the companion subpage for [[Template:Foo/testcases|test cases]]." local testcasesTitle = env.testcasesTitle if testcasesTitle and testcasesTitle.exists then if testcasesTitle.contentModel == "Scribunto" then local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display') local testcasesRunLinkDisplay = message('sandbox-notice-testcases-run-link-display') local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay) local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay) text = text .. '<br />' .. message('sandbox-notice-testcases-run-blurb', {testcasesLink, testcasesRunLink}) else local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display') local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay) text = text .. '<br />' .. message('sandbox-notice-testcases-blurb', {testcasesLink}) end end -- Add the sandbox to the sandbox category. omargs.text = text .. makeCategoryLink(message('sandbox-category')) -- 'documentation-clear' return '<div class="' .. message('clear') .. '"></div>' .. require('Module:Message box').main('ombox', omargs) end function p.protectionTemplate(env) -- Generates the padlock icon in the top right. -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'protection-template' --> 'pp-template' -- 'protection-template-args' --> {docusage = 'yes'} local protectionLevels = env.protectionLevels if not protectionLevels then return nil end local editProt = protectionLevels.edit and protectionLevels.edit[1] local moveProt = protectionLevels.move and protectionLevels.move[1] if editProt then -- The page is edit-protected. return require('Module:Protection banner')._main{ message('protection-reason-edit'), small = true } elseif moveProt and moveProt ~= 'autoconfirmed' then -- The page is move-protected but not edit-protected. Exclude move -- protection with the level "autoconfirmed", as this is equivalent to -- no move protection at all. return require('Module:Protection banner')._main{ action = 'move', small = true } else return nil end end ---------------------------------------------------------------------------- -- Start box ---------------------------------------------------------------------------- p.startBox = makeInvokeFunc('_startBox') function p._startBox(args, env) --[[ -- This function generates the start box. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make -- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox -- which generate the box HTML. --]] env = env or p.getEnvironment(args) local links local content = args.content if not content or args[1] then -- No need to include the links if the documentation is on the template page itself. local linksData = p.makeStartBoxLinksData(args, env) if linksData then links = p.renderStartBoxLinks(linksData) end end -- Generate the start box html. local data = p.makeStartBoxData(args, env, links) if data then return p.renderStartBox(data) else -- User specified no heading. return nil end end function p.makeStartBoxLinksData(args, env) --[[ -- Does initial processing of data to make the [view] [edit] [history] [purge] links. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'view-link-display' --> 'view' -- 'edit-link-display' --> 'edit' -- 'history-link-display' --> 'history' -- 'purge-link-display' --> 'purge' -- 'module-preload' --> 'Template:Documentation/preload-module-doc' -- 'docpage-preload' --> 'Template:Documentation/preload' -- 'create-link-display' --> 'create' --]] local subjectSpace = env.subjectSpace local title = env.title local docTitle = env.docTitle if not title or not docTitle then return nil end if docTitle.isRedirect then docTitle = docTitle.redirectTarget end local data = {} data.title = title data.docTitle = docTitle -- View, display, edit, and purge links if /doc exists. data.viewLinkDisplay = message('view-link-display') data.editLinkDisplay = message('edit-link-display') data.historyLinkDisplay = message('history-link-display') data.purgeLinkDisplay = message('purge-link-display') -- Create link if /doc doesn't exist. local preload = args.preload if not preload then if subjectSpace == 828 then -- Module namespace preload = message('module-preload') else preload = message('docpage-preload') end end data.preload = preload data.createLinkDisplay = message('create-link-display') return data end function p.renderStartBoxLinks(data) --[[ -- Generates the [view][edit][history][purge] or [create][purge] links from the data table. -- @data - a table of data generated by p.makeStartBoxLinksData --]] local docTitle = data.docTitle local purgeLink = makeWikilink("Special:Purge/" .. docTitle.prefixedText, data.purgeLinkDisplay) if docTitle.exists then local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay) local editLink = makeWikilink("Special:EditPage/" .. docTitle.prefixedText, data.editLinkDisplay) local historyLink = makeWikilink("Special:PageHistory/" .. docTitle.prefixedText, data.historyLinkDisplay) return "&#91;" .. viewLink .. "&#93; &#91;" .. editLink .. "&#93; &#91;" .. historyLink .. "&#93; &#91;" .. purgeLink .. "&#93;" else local createLink = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay) return "&#91;" .. createLink .. "&#93; &#91;" .. purgeLink .. "&#93;" end return ret end function p.makeStartBoxData(args, env, links) --[=[ -- Does initial processing of data to pass to the start-box render function, p.renderStartBox. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- @links - a string containing the [view][edit][history][purge] links - could be nil if there's an error. -- -- Messages: -- 'documentation-icon-wikitext' --> '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]' -- 'template-namespace-heading' --> 'Template documentation' -- 'module-namespace-heading' --> 'Module documentation' -- 'file-namespace-heading' --> 'Summary' -- 'other-namespaces-heading' --> 'Documentation' -- 'testcases-create-link-display' --> 'create' --]=] local subjectSpace = env.subjectSpace if not subjectSpace then -- Default to an "other namespaces" namespace, so that we get at least some output -- if an error occurs. subjectSpace = 2 end local data = {} -- Heading local heading = args.heading -- Blank values are not removed. if heading == '' then -- Don't display the start box if the heading arg is defined but blank. return nil end if heading then data.heading = heading elseif subjectSpace == 10 then -- Template namespace data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading') elseif subjectSpace == 828 then -- Module namespace data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading') elseif subjectSpace == 6 then -- File namespace data.heading = message('file-namespace-heading') else data.heading = message('other-namespaces-heading') end -- Heading CSS local headingStyle = args['heading-style'] if headingStyle then data.headingStyleText = headingStyle else -- 'documentation-heading' data.headingClass = message('main-div-heading-class') end -- Data for the [view][edit][history][purge] or [create] links. if links then -- 'mw-editsection-like plainlinks' data.linksClass = message('start-box-link-classes') data.links = links end return data end function p.renderStartBox(data) -- Renders the start box html. -- @data - a table of data generated by p.makeStartBoxData. local sbox = mw.html.create('div') sbox -- 'documentation-startbox' :addClass(message('start-box-class')) :newline() :tag('span') :addClass(data.headingClass) :attr('id', 'documentation-heading') :cssText(data.headingStyleText) :wikitext(data.heading) local links = data.links if links then sbox:tag('span') :addClass(data.linksClass) :attr('id', data.linksId) :wikitext(links) end return tostring(sbox) end ---------------------------------------------------------------------------- -- Documentation content ---------------------------------------------------------------------------- p.content = makeInvokeFunc('_content') function p._content(args, env) -- Displays the documentation contents -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment env = env or p.getEnvironment(args) local docTitle = env.docTitle local content = args.content if not content and docTitle and docTitle.exists then content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText} end -- The line breaks below are necessary so that "=== Headings ===" at the start and end -- of docs are interpreted correctly. return '\n' .. (content or '') .. '\n' end p.contentTitle = makeInvokeFunc('_contentTitle') function p._contentTitle(args, env) env = env or p.getEnvironment(args) local docTitle = env.docTitle if not args.content and docTitle and docTitle.exists then return docTitle.prefixedText else return '' end end ---------------------------------------------------------------------------- -- End box ---------------------------------------------------------------------------- p.endBox = makeInvokeFunc('_endBox') function p._endBox(args, env) --[=[ -- This function generates the end box (also known as the link box). -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- --]=] -- Get environment data. env = env or p.getEnvironment(args) local subjectSpace = env.subjectSpace local docTitle = env.docTitle if not subjectSpace or not docTitle then return nil end -- Check whether we should output the end box at all. Add the end -- box by default if the documentation exists or if we are in the -- user, module or template namespaces. local linkBox = args['link box'] if linkBox == 'off' or not ( docTitle.exists or subjectSpace == 2 or subjectSpace == 828 or subjectSpace == 10 ) then return nil end -- Assemble the link box. local text = '' if linkBox then text = text .. linkBox else text = text .. (p.makeDocPageBlurb(args, env) or '') -- "This documentation is transcluded from [[Foo]]." if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then -- We are in the user, template or module namespaces. -- Add sandbox and testcases links. -- "Editors can experiment in this template's sandbox and testcases pages." text = text .. (p.makeExperimentBlurb(args, env) or '') .. '<br />' if not args.content and not args[1] then -- "Please add categories to the /doc subpage." -- Don't show this message with inline docs or with an explicitly specified doc page, -- as then it is unclear where to add the categories. text = text .. (p.makeCategoriesBlurb(args, env) or '') end text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '') --"Subpages of this template" end end local box = mw.html.create('div') -- 'documentation-metadata' box:attr('role', 'note') :addClass(message('end-box-class')) -- 'plainlinks' :addClass(message('end-box-plainlinks')) :wikitext(text) :done() return '\n' .. tostring(box) end function p.makeDocPageBlurb(args, env) --[=[ -- Makes the blurb "This documentation is transcluded from [[Template:Foo]] (edit, history)". -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'edit-link-display' --> 'edit' -- 'history-link-display' --> 'history' -- 'transcluded-from-blurb' --> -- 'The above [[Wikipedia:Template documentation|documentation]] -- is [[Help:Transclusion|transcluded]] from $1.' -- 'module-preload' --> 'Template:Documentation/preload-module-doc' -- 'create-link-display' --> 'create' -- 'create-module-doc-blurb' --> -- 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].' --]=] local docTitle = env.docTitle if not docTitle then return nil end local ret if docTitle.exists then -- /doc exists; link to it. local docLink = makeWikilink(docTitle.prefixedText) local editDisplay = message('edit-link-display') local editLink = makeWikilink("Special:EditPage/" .. docTitle.prefixedText, editDisplay) local historyDisplay = message('history-link-display') local historyLink = makeWikilink("Special:PageHistory/" .. docTitle.prefixedText, historyDisplay) ret = message('transcluded-from-blurb', {docLink}) .. ' ' .. makeToolbar(editLink, historyLink) .. '<br />' elseif env.subjectSpace == 828 then -- /doc does not exist; ask to create it. local createUrl = docTitle:fullUrl{action = 'edit', preload = message('module-preload')} local createDisplay = message('create-link-display') local createLink = makeUrlLink(createUrl, createDisplay) ret = message('create-module-doc-blurb', {createLink}) .. '<br />' end return ret end function p.makeExperimentBlurb(args, env) --[[ -- Renders the text "Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages." -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- -- Messages: -- 'sandbox-link-display' --> 'sandbox' -- 'sandbox-edit-link-display' --> 'edit' -- 'compare-link-display' --> 'diff' -- 'module-sandbox-preload' --> 'Template:Documentation/preload-module-sandbox' -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox' -- 'sandbox-create-link-display' --> 'create' -- 'mirror-edit-summary' --> 'Create sandbox version of $1' -- 'mirror-link-display' --> 'mirror' -- 'mirror-link-preload' --> 'Template:Documentation/mirror' -- 'sandbox-link-display' --> 'sandbox' -- 'testcases-link-display' --> 'testcases' -- 'testcases-edit-link-display'--> 'edit' -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox' -- 'testcases-create-link-display' --> 'create' -- 'testcases-link-display' --> 'testcases' -- 'testcases-edit-link-display' --> 'edit' -- 'module-testcases-preload' --> 'Template:Documentation/preload-module-testcases' -- 'template-testcases-preload' --> 'Template:Documentation/preload-testcases' -- 'experiment-blurb-module' --> 'Editors can experiment in this module's $1 and $2 pages.' -- 'experiment-blurb-template' --> 'Editors can experiment in this template's $1 and $2 pages.' --]] local subjectSpace = env.subjectSpace local templateTitle = env.templateTitle local sandboxTitle = env.sandboxTitle local testcasesTitle = env.testcasesTitle local templatePage = templateTitle.prefixedText if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then return nil end -- Make links. local sandboxLinks, testcasesLinks if sandboxTitle.exists then local sandboxPage = sandboxTitle.prefixedText local sandboxDisplay = message('sandbox-link-display') local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay) local sandboxEditDisplay = message('sandbox-edit-link-display') local sandboxEditLink = makeWikilink("Special:EditPage/" .. sandboxPage, sandboxEditDisplay) local compareUrl = env.compareUrl local compareLink if compareUrl then local compareDisplay = message('compare-link-display') compareLink = makeUrlLink(compareUrl, compareDisplay) end sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink) else local sandboxPreload if subjectSpace == 828 then sandboxPreload = message('module-sandbox-preload') else sandboxPreload = message('template-sandbox-preload') end local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload} local sandboxCreateDisplay = message('sandbox-create-link-display') local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay) local mirrorSummary = message('mirror-edit-summary', {makeWikilink(templatePage)}) local mirrorPreload = message('mirror-link-preload') local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = mirrorPreload, summary = mirrorSummary} if subjectSpace == 828 then mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templateTitle.prefixedText, summary = mirrorSummary} end local mirrorDisplay = message('mirror-link-display') local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay) sandboxLinks = message('sandbox-link-display') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink) end if testcasesTitle.exists then local testcasesPage = testcasesTitle.prefixedText local testcasesDisplay = message('testcases-link-display') local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay) local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'} local testcasesEditDisplay = message('testcases-edit-link-display') local testcasesEditLink = makeWikilink("Special:EditPage/" .. testcasesPage, testcasesEditDisplay) -- for Modules, add testcases run link if exists if testcasesTitle.contentModel == "Scribunto" and testcasesTitle.talkPageTitle and testcasesTitle.talkPageTitle.exists then local testcasesRunLinkDisplay = message('testcases-run-link-display') local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay) testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink, testcasesRunLink) else testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink) end else local testcasesPreload if subjectSpace == 828 then testcasesPreload = message('module-testcases-preload') else testcasesPreload = message('template-testcases-preload') end local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload} local testcasesCreateDisplay = message('testcases-create-link-display') local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay) testcasesLinks = message('testcases-link-display') .. ' ' .. makeToolbar(testcasesCreateLink) end local messageName if subjectSpace == 828 then messageName = 'experiment-blurb-module' else messageName = 'experiment-blurb-template' end return message(messageName, {sandboxLinks, testcasesLinks}) end function p.makeCategoriesBlurb(args, env) --[[ -- Generates the text "Please add categories to the /doc subpage." -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'doc-link-display' --> '/doc' -- 'add-categories-blurb' --> 'Please add categories to the $1 subpage.' --]] local docTitle = env.docTitle if not docTitle then return nil end local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display')) return message('add-categories-blurb', {docPathLink}) end function p.makeSubpagesBlurb(args, env) --[[ -- Generates the "Subpages of this template" link. -- @args - a table of arguments passed by the user -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'template-pagetype' --> 'template' -- 'module-pagetype' --> 'module' -- 'default-pagetype' --> 'page' -- 'subpages-link-display' --> 'Subpages of this $1' --]] local subjectSpace = env.subjectSpace local templateTitle = env.templateTitle if not subjectSpace or not templateTitle then return nil end local pagetype if subjectSpace == 10 then pagetype = message('template-pagetype') elseif subjectSpace == 828 then pagetype = message('module-pagetype') else pagetype = message('default-pagetype') end local subpagesLink = makeWikilink( 'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/', message('subpages-link-display', {pagetype}) ) return message('subpages-blurb', {subpagesLink}) end ---------------------------------------------------------------------------- -- Tracking categories ---------------------------------------------------------------------------- function p.addTrackingCategories(env) --[[ -- Check if {{documentation}} is transcluded on a /doc or /testcases page. -- @env - environment table containing title objects, etc., generated with p.getEnvironment -- Messages: -- 'display-strange-usage-category' --> true -- 'doc-subpage' --> 'doc' -- 'testcases-subpage' --> 'testcases' -- 'strange-usage-category' --> 'Wikipedia pages with strange ((documentation)) usage' -- -- /testcases pages in the module namespace are not categorised, as they may have -- {{documentation}} transcluded automatically. --]] local title = env.title local subjectSpace = env.subjectSpace if not title or not subjectSpace then return nil end local subpage = title.subpageText local ret = '' if message('display-strange-usage-category', nil, 'boolean') and ( subpage == message('doc-subpage') or subjectSpace ~= 828 and subpage == message('testcases-subpage') ) then ret = ret .. makeCategoryLink(message('strange-usage-category')) end return ret end return p 728908c27cb1e65ac402f1af8a9e4f144ddc3d0d Module:Yesno 828 345 685 684 2023-07-10T15:44:37Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Yesno]] Scribunto text/plain -- Function allowing for consistent treatment of boolean-like wikitext input. -- It works similarly to the template {{yesno}}. return function (val, default) -- If your wiki uses non-ascii characters for any of "yes", "no", etc., you -- should replace "val:lower()" with "mw.ustring.lower(val)" in the -- following line. val = type(val) == 'string' and val:lower() or val if val == nil then return nil elseif val == true or val == 'yes' or val == 'y' or val == 'true' or val == 't' or val == 'on' or tonumber(val) == 1 then return true elseif val == false or val == 'no' or val == 'n' or val == 'false' or val == 'f' or val == 'off' or tonumber(val) == 0 then return false else return default end end f767643e7d12126d020d88d662a3dd057817b9dc Module:Arguments 828 346 687 686 2023-07-10T15:44:37Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Arguments]] Scribunto text/plain -- This module provides easy processing of arguments passed to Scribunto from -- #invoke. It is intended for use by other Lua modules, and should not be -- called from #invoke directly. local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local arguments = {} -- Generate four different tidyVal functions, so that we don't have to check the -- options every time we call it. local function tidyValDefault(key, val) if type(val) == 'string' then val = val:match('^%s*(.-)%s*$') if val == '' then return nil else return val end else return val end end local function tidyValTrimOnly(key, val) if type(val) == 'string' then return val:match('^%s*(.-)%s*$') else return val end end local function tidyValRemoveBlanksOnly(key, val) if type(val) == 'string' then if val:find('%S') then return val else return nil end else return val end end local function tidyValNoChange(key, val) return val end local function matchesTitle(given, title) local tp = type( given ) return (tp == 'string' or tp == 'number') and mw.title.new( given ).prefixedText == title end local translate_mt = { __index = function(t, k) return k end } function arguments.getArgs(frame, options) checkType('getArgs', 1, frame, 'table', true) checkType('getArgs', 2, options, 'table', true) frame = frame or {} options = options or {} --[[ -- Set up argument translation. --]] options.translate = options.translate or {} if getmetatable(options.translate) == nil then setmetatable(options.translate, translate_mt) end if options.backtranslate == nil then options.backtranslate = {} for k,v in pairs(options.translate) do options.backtranslate[v] = k end end if options.backtranslate and getmetatable(options.backtranslate) == nil then setmetatable(options.backtranslate, { __index = function(t, k) if options.translate[k] ~= k then return nil else return k end end }) end --[[ -- Get the argument tables. If we were passed a valid frame object, get the -- frame arguments (fargs) and the parent frame arguments (pargs), depending -- on the options set and on the parent frame's availability. If we weren't -- passed a valid frame object, we are being called from another Lua module -- or from the debug console, so assume that we were passed a table of args -- directly, and assign it to a new variable (luaArgs). --]] local fargs, pargs, luaArgs if type(frame.args) == 'table' and type(frame.getParent) == 'function' then if options.wrappers then --[[ -- The wrappers option makes Module:Arguments look up arguments in -- either the frame argument table or the parent argument table, but -- not both. This means that users can use either the #invoke syntax -- or a wrapper template without the loss of performance associated -- with looking arguments up in both the frame and the parent frame. -- Module:Arguments will look up arguments in the parent frame -- if it finds the parent frame's title in options.wrapper; -- otherwise it will look up arguments in the frame object passed -- to getArgs. --]] local parent = frame:getParent() if not parent then fargs = frame.args else local title = parent:getTitle():gsub('/sandbox$', '') local found = false if matchesTitle(options.wrappers, title) then found = true elseif type(options.wrappers) == 'table' then for _,v in pairs(options.wrappers) do if matchesTitle(v, title) then found = true break end end end -- We test for false specifically here so that nil (the default) acts like true. if found or options.frameOnly == false then pargs = parent.args end if not found or options.parentOnly == false then fargs = frame.args end end else -- options.wrapper isn't set, so check the other options. if not options.parentOnly then fargs = frame.args end if not options.frameOnly then local parent = frame:getParent() pargs = parent and parent.args or nil end end if options.parentFirst then fargs, pargs = pargs, fargs end else luaArgs = frame end -- Set the order of precedence of the argument tables. If the variables are -- nil, nothing will be added to the table, which is how we avoid clashes -- between the frame/parent args and the Lua args. local argTables = {fargs} argTables[#argTables + 1] = pargs argTables[#argTables + 1] = luaArgs --[[ -- Generate the tidyVal function. If it has been specified by the user, we -- use that; if not, we choose one of four functions depending on the -- options chosen. This is so that we don't have to call the options table -- every time the function is called. --]] local tidyVal = options.valueFunc if tidyVal then if type(tidyVal) ~= 'function' then error( "bad value assigned to option 'valueFunc'" .. '(function expected, got ' .. type(tidyVal) .. ')', 2 ) end elseif options.trim ~= false then if options.removeBlanks ~= false then tidyVal = tidyValDefault else tidyVal = tidyValTrimOnly end else if options.removeBlanks ~= false then tidyVal = tidyValRemoveBlanksOnly else tidyVal = tidyValNoChange end end --[[ -- Set up the args, metaArgs and nilArgs tables. args will be the one -- accessed from functions, and metaArgs will hold the actual arguments. Nil -- arguments are memoized in nilArgs, and the metatable connects all of them -- together. --]] local args, metaArgs, nilArgs, metatable = {}, {}, {}, {} setmetatable(args, metatable) local function mergeArgs(tables) --[[ -- Accepts multiple tables as input and merges their keys and values -- into one table. If a value is already present it is not overwritten; -- tables listed earlier have precedence. We are also memoizing nil -- values, which can be overwritten if they are 's' (soft). --]] for _, t in ipairs(tables) do for key, val in pairs(t) do if metaArgs[key] == nil and nilArgs[key] ~= 'h' then local tidiedVal = tidyVal(key, val) if tidiedVal == nil then nilArgs[key] = 's' else metaArgs[key] = tidiedVal end end end end end --[[ -- Define metatable behaviour. Arguments are memoized in the metaArgs table, -- and are only fetched from the argument tables once. Fetching arguments -- from the argument tables is the most resource-intensive step in this -- module, so we try and avoid it where possible. For this reason, nil -- arguments are also memoized, in the nilArgs table. Also, we keep a record -- in the metatable of when pairs and ipairs have been called, so we do not -- run pairs and ipairs on the argument tables more than once. We also do -- not run ipairs on fargs and pargs if pairs has already been run, as all -- the arguments will already have been copied over. --]] metatable.__index = function (t, key) --[[ -- Fetches an argument when the args table is indexed. First we check -- to see if the value is memoized, and if not we try and fetch it from -- the argument tables. When we check memoization, we need to check -- metaArgs before nilArgs, as both can be non-nil at the same time. -- If the argument is not present in metaArgs, we also check whether -- pairs has been run yet. If pairs has already been run, we return nil. -- This is because all the arguments will have already been copied into -- metaArgs by the mergeArgs function, meaning that any other arguments -- must be nil. --]] if type(key) == 'string' then key = options.translate[key] end local val = metaArgs[key] if val ~= nil then return val elseif metatable.donePairs or nilArgs[key] then return nil end for _, argTable in ipairs(argTables) do local argTableVal = tidyVal(key, argTable[key]) if argTableVal ~= nil then metaArgs[key] = argTableVal return argTableVal end end nilArgs[key] = 'h' return nil end metatable.__newindex = function (t, key, val) -- This function is called when a module tries to add a new value to the -- args table, or tries to change an existing value. if type(key) == 'string' then key = options.translate[key] end if options.readOnly then error( 'could not write to argument table key "' .. tostring(key) .. '"; the table is read-only', 2 ) elseif options.noOverwrite and args[key] ~= nil then error( 'could not write to argument table key "' .. tostring(key) .. '"; overwriting existing arguments is not permitted', 2 ) elseif val == nil then --[[ -- If the argument is to be overwritten with nil, we need to erase -- the value in metaArgs, so that __index, __pairs and __ipairs do -- not use a previous existing value, if present; and we also need -- to memoize the nil in nilArgs, so that the value isn't looked -- up in the argument tables if it is accessed again. --]] metaArgs[key] = nil nilArgs[key] = 'h' else metaArgs[key] = val end end local function translatenext(invariant) local k, v = next(invariant.t, invariant.k) invariant.k = k if k == nil then return nil elseif type(k) ~= 'string' or not options.backtranslate then return k, v else local backtranslate = options.backtranslate[k] if backtranslate == nil then -- Skip this one. This is a tail call, so this won't cause stack overflow return translatenext(invariant) else return backtranslate, v end end end metatable.__pairs = function () -- Called when pairs is run on the args table. if not metatable.donePairs then mergeArgs(argTables) metatable.donePairs = true end return translatenext, { t = metaArgs } end local function inext(t, i) -- This uses our __index metamethod local v = t[i + 1] if v ~= nil then return i + 1, v end end metatable.__ipairs = function (t) -- Called when ipairs is run on the args table. return inext, t, 0 end return args end return arguments 3134ecce8429b810d445e29eae115e2ae4c36c53 Module:Tl 828 347 689 688 2023-07-10T15:44:37Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Tl]] wikitext text/x-wiki #REDIRECT [[Template:Template link]] {{Redirect category shell| {{R from move}} }} d6593bb3b4a866249f55d0f34b047a71fe1f1529 Module:Template link 828 348 691 690 2023-07-10T15:44:38Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Template_link]] wikitext text/x-wiki &#123;&#123;[[Template:{{{1}}}|{{{1}}}]]&#125;&#125;<noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> eabbec62efe3044a98ebb3ce9e7d4d43c222351d Module:Message box 828 349 693 692 2023-07-10T15:44:38Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Message_box]] Scribunto text/plain require('strict') local getArgs local yesno = require('Module:Yesno') local lang = mw.language.getContentLanguage() local CONFIG_MODULE = 'Module:Message box/configuration' local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function getTitleObject(...) -- Get the title object, passing the function through pcall -- in case we are over the expensive function count limit. local success, title = pcall(mw.title.new, ...) if success then return title end end local function union(t1, t2) -- Returns the union of two arrays. local vals = {} for i, v in ipairs(t1) do vals[v] = true end for i, v in ipairs(t2) do vals[v] = true end local ret = {} for k in pairs(vals) do table.insert(ret, k) end table.sort(ret) return ret end local function getArgNums(args, prefix) local nums = {} for k, v in pairs(args) do local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$') if num then table.insert(nums, tonumber(num)) end end table.sort(nums) return nums end -------------------------------------------------------------------------------- -- Box class definition -------------------------------------------------------------------------------- local MessageBox = {} MessageBox.__index = MessageBox function MessageBox.new(boxType, args, cfg) args = args or {} local obj = {} -- Set the title object and the namespace. obj.title = getTitleObject(args.page) or mw.title.getCurrentTitle() -- Set the config for our box type. obj.cfg = cfg[boxType] if not obj.cfg then local ns = obj.title.namespace -- boxType is "mbox" or invalid input if args.demospace and args.demospace ~= '' then -- implement demospace parameter of mbox local demospace = string.lower(args.demospace) if DEMOSPACES[demospace] then -- use template from DEMOSPACES obj.cfg = cfg[DEMOSPACES[demospace]] elseif string.find( demospace, 'talk' ) then -- demo as a talk page obj.cfg = cfg.tmbox else -- default to ombox obj.cfg = cfg.ombox end elseif ns == 0 then obj.cfg = cfg.ambox -- main namespace elseif ns == 6 then obj.cfg = cfg.imbox -- file namespace elseif ns == 14 then obj.cfg = cfg.cmbox -- category namespace else local nsTable = mw.site.namespaces[ns] if nsTable and nsTable.isTalk then obj.cfg = cfg.tmbox -- any talk namespace else obj.cfg = cfg.ombox -- other namespaces or invalid input end end end -- Set the arguments, and remove all blank arguments except for the ones -- listed in cfg.allowBlankParams. do local newArgs = {} for k, v in pairs(args) do if v ~= '' then newArgs[k] = v end end for i, param in ipairs(obj.cfg.allowBlankParams or {}) do newArgs[param] = args[param] end obj.args = newArgs end -- Define internal data structure. obj.categories = {} obj.classes = {} -- For lazy loading of [[Module:Category handler]]. obj.hasCategories = false return setmetatable(obj, MessageBox) end function MessageBox:addCat(ns, cat, sort) if not cat then return nil end if sort then cat = string.format('[[Category:%s|%s]]', cat, sort) else cat = string.format('[[Category:%s]]', cat) end self.hasCategories = true self.categories[ns] = self.categories[ns] or {} table.insert(self.categories[ns], cat) end function MessageBox:addClass(class) if not class then return nil end table.insert(self.classes, class) end function MessageBox:setParameters() local args = self.args local cfg = self.cfg -- Get type data. self.type = args.type local typeData = cfg.types[self.type] self.invalidTypeError = cfg.showInvalidTypeError and self.type and not typeData typeData = typeData or cfg.types[cfg.default] self.typeClass = typeData.class self.typeImage = typeData.image -- Find if the box has been wrongly substituted. self.isSubstituted = cfg.substCheck and args.subst == 'SUBST' -- Find whether we are using a small message box. self.isSmall = cfg.allowSmall and ( cfg.smallParam and args.small == cfg.smallParam or not cfg.smallParam and yesno(args.small) ) -- Add attributes, classes and styles. self.id = args.id self.name = args.name if self.name then self:addClass('box-' .. string.gsub(self.name,' ','_')) end if yesno(args.plainlinks) ~= false then self:addClass('plainlinks') end for _, class in ipairs(cfg.classes or {}) do self:addClass(class) end if self.isSmall then self:addClass(cfg.smallClass or 'mbox-small') end self:addClass(self.typeClass) self:addClass(args.class) self.style = args.style self.attrs = args.attrs -- Set text style. self.textstyle = args.textstyle -- Find if we are on the template page or not. This functionality is only -- used if useCollapsibleTextFields is set, or if both cfg.templateCategory -- and cfg.templateCategoryRequireName are set. self.useCollapsibleTextFields = cfg.useCollapsibleTextFields if self.useCollapsibleTextFields or cfg.templateCategory and cfg.templateCategoryRequireName then if self.name then local templateName = mw.ustring.match( self.name, '^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$' ) or self.name templateName = 'Template:' .. templateName self.templateTitle = getTitleObject(templateName) end self.isTemplatePage = self.templateTitle and mw.title.equals(self.title, self.templateTitle) end -- Process data for collapsible text fields. At the moment these are only -- used in {{ambox}}. if self.useCollapsibleTextFields then -- Get the self.issue value. if self.isSmall and args.smalltext then self.issue = args.smalltext else local sect if args.sect == '' then sect = 'This ' .. (cfg.sectionDefault or 'page') elseif type(args.sect) == 'string' then sect = 'This ' .. args.sect end local issue = args.issue issue = type(issue) == 'string' and issue ~= '' and issue or nil local text = args.text text = type(text) == 'string' and text or nil local issues = {} table.insert(issues, sect) table.insert(issues, issue) table.insert(issues, text) self.issue = table.concat(issues, ' ') end -- Get the self.talk value. local talk = args.talk -- Show talk links on the template page or template subpages if the talk -- parameter is blank. if talk == '' and self.templateTitle and ( mw.title.equals(self.templateTitle, self.title) or self.title:isSubpageOf(self.templateTitle) ) then talk = '#' elseif talk == '' then talk = nil end if talk then -- If the talk value is a talk page, make a link to that page. Else -- assume that it's a section heading, and make a link to the talk -- page of the current page with that section heading. local talkTitle = getTitleObject(talk) local talkArgIsTalkPage = true if not talkTitle or not talkTitle.isTalkPage then talkArgIsTalkPage = false talkTitle = getTitleObject( self.title.text, mw.site.namespaces[self.title.namespace].talk.id ) end if talkTitle and talkTitle.exists then local talkText if self.isSmall then local talkLink = talkArgIsTalkPage and talk or (talkTitle.prefixedText .. '#' .. talk) talkText = string.format('([[%s|talk]])', talkLink) else talkText = 'Relevant discussion may be found on' if talkArgIsTalkPage then talkText = string.format( '%s [[%s|%s]].', talkText, talk, talkTitle.prefixedText ) else talkText = string.format( '%s the [[%s#%s|talk page]].', talkText, talkTitle.prefixedText, talk ) end end self.talk = talkText end end -- Get other values. self.fix = args.fix ~= '' and args.fix or nil local date if args.date and args.date ~= '' then date = args.date elseif args.date == '' and self.isTemplatePage then date = lang:formatDate('F Y') end if date then self.date = string.format(" <span class='date-container'><i>(<span class='date'>%s</span>)</i></span>", date) end self.info = args.info if yesno(args.removalnotice) then self.removalNotice = cfg.removalNotice end end -- Set the non-collapsible text field. At the moment this is used by all box -- types other than ambox, and also by ambox when small=yes. if self.isSmall then self.text = args.smalltext or args.text else self.text = args.text end -- Set the below row. self.below = cfg.below and args.below -- General image settings. self.imageCellDiv = not self.isSmall and cfg.imageCellDiv self.imageEmptyCell = cfg.imageEmptyCell -- Left image settings. local imageLeft = self.isSmall and args.smallimage or args.image if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none' or not cfg.imageCheckBlank and imageLeft ~= 'none' then self.imageLeft = imageLeft if not imageLeft then local imageSize = self.isSmall and (cfg.imageSmallSize or '30x30px') or '40x40px' self.imageLeft = string.format('[[File:%s|%s|link=|alt=]]', self.typeImage or 'Imbox notice.png', imageSize) end end -- Right image settings. local imageRight = self.isSmall and args.smallimageright or args.imageright if not (cfg.imageRightNone and imageRight == 'none') then self.imageRight = imageRight end -- set templatestyles self.base_templatestyles = cfg.templatestyles self.templatestyles = args.templatestyles end function MessageBox:setMainspaceCategories() local args = self.args local cfg = self.cfg if not cfg.allowMainspaceCategories then return nil end local nums = {} for _, prefix in ipairs{'cat', 'category', 'all'} do args[prefix .. '1'] = args[prefix] nums = union(nums, getArgNums(args, prefix)) end -- The following is roughly equivalent to the old {{Ambox/category}}. local date = args.date date = type(date) == 'string' and date local preposition = 'from' for _, num in ipairs(nums) do local mainCat = args['cat' .. tostring(num)] or args['category' .. tostring(num)] local allCat = args['all' .. tostring(num)] mainCat = type(mainCat) == 'string' and mainCat allCat = type(allCat) == 'string' and allCat if mainCat and date and date ~= '' then local catTitle = string.format('%s %s %s', mainCat, preposition, date) self:addCat(0, catTitle) catTitle = getTitleObject('Category:' .. catTitle) if not catTitle or not catTitle.exists then self:addCat(0, 'Articles with invalid date parameter in template') end elseif mainCat and (not date or date == '') then self:addCat(0, mainCat) end if allCat then self:addCat(0, allCat) end end end function MessageBox:setTemplateCategories() local args = self.args local cfg = self.cfg -- Add template categories. if cfg.templateCategory then if cfg.templateCategoryRequireName then if self.isTemplatePage then self:addCat(10, cfg.templateCategory) end elseif not self.title.isSubpage then self:addCat(10, cfg.templateCategory) end end -- Add template error categories. if cfg.templateErrorCategory then local templateErrorCategory = cfg.templateErrorCategory local templateCat, templateSort if not self.name and not self.title.isSubpage then templateCat = templateErrorCategory elseif self.isTemplatePage then local paramsToCheck = cfg.templateErrorParamsToCheck or {} local count = 0 for i, param in ipairs(paramsToCheck) do if not args[param] then count = count + 1 end end if count > 0 then templateCat = templateErrorCategory templateSort = tostring(count) end if self.categoryNums and #self.categoryNums > 0 then templateCat = templateErrorCategory templateSort = 'C' end end self:addCat(10, templateCat, templateSort) end end function MessageBox:setAllNamespaceCategories() -- Set categories for all namespaces. if self.invalidTypeError then local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort) end if self.isSubstituted then self:addCat('all', 'Pages with incorrectly substituted templates') end end function MessageBox:setCategories() if self.title.namespace == 0 then self:setMainspaceCategories() elseif self.title.namespace == 10 then self:setTemplateCategories() end self:setAllNamespaceCategories() end function MessageBox:renderCategories() if not self.hasCategories then -- No categories added, no need to pass them to Category handler so, -- if it was invoked, it would return the empty string. -- So we shortcut and return the empty string. return "" end -- Convert category tables to strings and pass them through -- [[Module:Category handler]]. return require('Module:Category handler')._main{ main = table.concat(self.categories[0] or {}), template = table.concat(self.categories[10] or {}), all = table.concat(self.categories.all or {}), nocat = self.args.nocat, page = self.args.page } end function MessageBox:export() local root = mw.html.create() -- Add the subst check error. if self.isSubstituted and self.name then root:tag('b') :addClass('error') :wikitext(string.format( 'Template <code>%s[[Template:%s|%s]]%s</code> has been incorrectly substituted.', mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}') )) end local frame = mw.getCurrentFrame() root:wikitext(frame:extensionTag{ name = 'templatestyles', args = { src = self.base_templatestyles }, }) -- Add support for a single custom templatestyles sheet. Undocumented as -- need should be limited and many templates using mbox are substed; we -- don't want to spread templatestyles sheets around to arbitrary places if self.templatestyles then root:wikitext(frame:extensionTag{ name = 'templatestyles', args = { src = self.templatestyles }, }) end -- Create the box table. local boxTable = root:tag('table') boxTable:attr('id', self.id or nil) for i, class in ipairs(self.classes or {}) do boxTable:addClass(class or nil) end boxTable :cssText(self.style or nil) :attr('role', 'presentation') if self.attrs then boxTable:attr(self.attrs) end -- Add the left-hand image. local row = boxTable:tag('tr') if self.imageLeft then local imageLeftCell = row:tag('td'):addClass('mbox-image') if self.imageCellDiv then -- If we are using a div, redefine imageLeftCell so that the image -- is inside it. Divs use style="width: 52px;", which limits the -- image width to 52px. If any images in a div are wider than that, -- they may overlap with the text or cause other display problems. imageLeftCell = imageLeftCell:tag('div'):addClass('mbox-image-div') end imageLeftCell:wikitext(self.imageLeft or nil) elseif self.imageEmptyCell then -- Some message boxes define an empty cell if no image is specified, and -- some don't. The old template code in templates where empty cells are -- specified gives the following hint: "No image. Cell with some width -- or padding necessary for text cell to have 100% width." row:tag('td') :addClass('mbox-empty-cell') end -- Add the text. local textCell = row:tag('td'):addClass('mbox-text') if self.useCollapsibleTextFields then -- The message box uses advanced text parameters that allow things to be -- collapsible. At the moment, only ambox uses this. textCell:cssText(self.textstyle or nil) local textCellDiv = textCell:tag('div') textCellDiv :addClass('mbox-text-span') :wikitext(self.issue or nil) if (self.talk or self.fix) then textCellDiv:tag('span') :addClass('hide-when-compact') :wikitext(self.talk and (' ' .. self.talk) or nil) :wikitext(self.fix and (' ' .. self.fix) or nil) end textCellDiv:wikitext(self.date and (' ' .. self.date) or nil) if self.info and not self.isSmall then textCellDiv :tag('span') :addClass('hide-when-compact') :wikitext(self.info and (' ' .. self.info) or nil) end if self.removalNotice then textCellDiv:tag('span') :addClass('hide-when-compact') :tag('i') :wikitext(string.format(" (%s)", self.removalNotice)) end else -- Default text formatting - anything goes. textCell :cssText(self.textstyle or nil) :wikitext(self.text or nil) end -- Add the right-hand image. if self.imageRight then local imageRightCell = row:tag('td'):addClass('mbox-imageright') if self.imageCellDiv then -- If we are using a div, redefine imageRightCell so that the image -- is inside it. imageRightCell = imageRightCell:tag('div'):addClass('mbox-image-div') end imageRightCell :wikitext(self.imageRight or nil) end -- Add the below row. if self.below then boxTable:tag('tr') :tag('td') :attr('colspan', self.imageRight and '3' or '2') :addClass('mbox-text') :cssText(self.textstyle or nil) :wikitext(self.below or nil) end -- Add error message for invalid type parameters. if self.invalidTypeError then root:tag('div') :addClass('mbox-invalid-type') :wikitext(string.format( 'This message box is using an invalid "type=%s" parameter and needs fixing.', self.type or '' )) end -- Add categories. root:wikitext(self:renderCategories() or nil) return tostring(root) end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p, mt = {}, {} function p._exportClasses() -- For testing. return { MessageBox = MessageBox } end function p.main(boxType, args, cfgTables) local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE)) box:setParameters() box:setCategories() return box:export() end function mt.__index(t, k) return function (frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end return t.main(k, getArgs(frame, {trim = false, removeBlanks = false})) end end return setmetatable(p, mt) bdb0ecc9f26f26b9c0ce12a066a183ac9d4f0705 Module:Message box/configuration 828 350 695 694 2023-07-10T15:44:38Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Message_box/configuration]] Scribunto text/plain -------------------------------------------------------------------------------- -- Message box configuration -- -- -- -- This module contains configuration data for [[Module:Message box]]. -- -------------------------------------------------------------------------------- return { ambox = { types = { speedy = { class = 'ambox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'ambox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'ambox-content', image = 'Ambox important.svg' }, style = { class = 'ambox-style', image = 'Edit-clear.svg' }, move = { class = 'ambox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'ambox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'ambox-notice', image = 'Information icon4.svg' } }, default = 'notice', allowBlankParams = {'talk', 'sect', 'date', 'issue', 'fix', 'subst', 'hidden'}, allowSmall = true, smallParam = 'left', smallClass = 'mbox-small-left', substCheck = true, classes = {'metadata', 'ambox'}, imageEmptyCell = true, imageCheckBlank = true, imageSmallSize = '20x20px', imageCellDiv = true, useCollapsibleTextFields = true, imageRightNone = true, sectionDefault = 'article', allowMainspaceCategories = true, templateCategory = 'Article message templates', templateCategoryRequireName = true, templateErrorCategory = 'Article message templates with missing parameters', templateErrorParamsToCheck = {'issue', 'fix', 'subst'}, removalNotice = '<small>[[Help:Maintenance template removal|Learn how and when to remove this template message]]</small>', templatestyles = 'Module:Message box/ambox.css' }, cmbox = { types = { speedy = { class = 'cmbox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'cmbox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'cmbox-content', image = 'Ambox important.svg' }, style = { class = 'cmbox-style', image = 'Edit-clear.svg' }, move = { class = 'cmbox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'cmbox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'cmbox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'cmbox'}, imageEmptyCell = true, templatestyles = 'Module:Message box/cmbox.css' }, fmbox = { types = { warning = { class = 'fmbox-warning', image = 'Ambox warning pn.svg' }, editnotice = { class = 'fmbox-editnotice', image = 'Information icon4.svg' }, system = { class = 'fmbox-system', image = 'Information icon4.svg' } }, default = 'system', showInvalidTypeError = true, classes = {'fmbox'}, imageEmptyCell = false, imageRightNone = false, templatestyles = 'Module:Message box/fmbox.css' }, imbox = { types = { speedy = { class = 'imbox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'imbox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'imbox-content', image = 'Ambox important.svg' }, style = { class = 'imbox-style', image = 'Edit-clear.svg' }, move = { class = 'imbox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'imbox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, license = { class = 'imbox-license licensetpl', image = 'Imbox license.png' -- @todo We need an SVG version of this }, featured = { class = 'imbox-featured', image = 'Cscr-featured.svg' }, notice = { class = 'imbox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'imbox'}, imageEmptyCell = true, below = true, templateCategory = 'File message boxes', templatestyles = 'Module:Message box/imbox.css' }, ombox = { types = { speedy = { class = 'ombox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'ombox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'ombox-content', image = 'Ambox important.svg' }, style = { class = 'ombox-style', image = 'Edit-clear.svg' }, move = { class = 'ombox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'ombox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'ombox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'ombox'}, allowSmall = true, imageEmptyCell = true, imageRightNone = true, templatestyles = 'Module:Message box/ombox.css' }, tmbox = { types = { speedy = { class = 'tmbox-speedy', image = 'Ambox warning pn.svg' }, delete = { class = 'tmbox-delete', image = 'Ambox warning pn.svg' }, content = { class = 'tmbox-content', image = 'Ambox important.svg' }, style = { class = 'tmbox-style', image = 'Edit-clear.svg' }, move = { class = 'tmbox-move', image = 'Merge-split-transwiki default.svg' }, protection = { class = 'tmbox-protection', image = 'Semi-protection-shackle-keyhole.svg' }, notice = { class = 'tmbox-notice', image = 'Information icon4.svg' } }, default = 'notice', showInvalidTypeError = true, classes = {'tmbox'}, allowSmall = true, imageRightNone = true, imageEmptyCell = true, templateCategory = 'Talk message boxes', templatestyles = 'Module:Message box/tmbox.css' } } b6f0151037e6867b577c8cca32ff297e48697a10 Module:String 828 351 697 696 2023-07-10T15:44:39Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:String]] Scribunto text/plain --[[ This module is intended to provide access to basic string functions. Most of the functions provided here can be invoked with named parameters, unnamed parameters, or a mixture. If named parameters are used, Mediawiki will automatically remove any leading or trailing whitespace from the parameter. Depending on the intended use, it may be advantageous to either preserve or remove such whitespace. Global options ignore_errors: If set to 'true' or 1, any error condition will result in an empty string being returned rather than an error message. error_category: If an error occurs, specifies the name of a category to include with the error message. The default category is [Category:Errors reported by Module String]. no_category: If set to 'true' or 1, no category will be added if an error is generated. Unit tests for this module are available at Module:String/tests. ]] local str = {} --[[ len This function returns the length of the target string. Usage: {{#invoke:String|len|target_string|}} OR {{#invoke:String|len|s=target_string}} Parameters s: The string whose length to report If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from the target string. ]] function str.len( frame ) local new_args = str._getParameters( frame.args, {'s'} ) local s = new_args['s'] or '' return mw.ustring.len( s ) end --[[ sub This function returns a substring of the target string at specified indices. Usage: {{#invoke:String|sub|target_string|start_index|end_index}} OR {{#invoke:String|sub|s=target_string|i=start_index|j=end_index}} Parameters s: The string to return a subset of i: The fist index of the substring to return, defaults to 1. j: The last index of the string to return, defaults to the last character. The first character of the string is assigned an index of 1. If either i or j is a negative value, it is interpreted the same as selecting a character by counting from the end of the string. Hence, a value of -1 is the same as selecting the last character of the string. If the requested indices are out of range for the given string, an error is reported. ]] function str.sub( frame ) local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } ) local s = new_args['s'] or '' local i = tonumber( new_args['i'] ) or 1 local j = tonumber( new_args['j'] ) or -1 local len = mw.ustring.len( s ) -- Convert negatives for range checking if i < 0 then i = len + i + 1 end if j < 0 then j = len + j + 1 end if i > len or j > len or i < 1 or j < 1 then return str._error( 'String subset index out of range' ) end if j < i then return str._error( 'String subset indices out of order' ) end return mw.ustring.sub( s, i, j ) end --[[ This function implements that features of {{str sub old}} and is kept in order to maintain these older templates. ]] function str.sublength( frame ) local i = tonumber( frame.args.i ) or 0 local len = tonumber( frame.args.len ) return mw.ustring.sub( frame.args.s, i + 1, len and ( i + len ) ) end --[[ _match This function returns a substring from the source string that matches a specified pattern. It is exported for use in other modules Usage: strmatch = require("Module:String")._match sresult = strmatch( s, pattern, start, match, plain, nomatch ) Parameters s: The string to search pattern: The pattern or string to find within the string start: The index within the source string to start the search. The first character of the string has index 1. Defaults to 1. match: In some cases it may be possible to make multiple matches on a single string. This specifies which match to return, where the first match is match= 1. If a negative number is specified then a match is returned counting from the last match. Hence match = -1 is the same as requesting the last match. Defaults to 1. plain: A flag indicating that the pattern should be understood as plain text. Defaults to false. nomatch: If no match is found, output the "nomatch" value rather than an error. For information on constructing Lua patterns, a form of [regular expression], see: * http://www.lua.org/manual/5.1/manual.html#5.4.1 * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns ]] -- This sub-routine is exported for use in other modules function str._match( s, pattern, start, match_index, plain_flag, nomatch ) if s == '' then return str._error( 'Target string is empty' ) end if pattern == '' then return str._error( 'Pattern string is empty' ) end start = tonumber(start) or 1 if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then return str._error( 'Requested start is out of range' ) end if match_index == 0 then return str._error( 'Match index is out of range' ) end if plain_flag then pattern = str._escapePattern( pattern ) end local result if match_index == 1 then -- Find first match is simple case result = mw.ustring.match( s, pattern, start ) else if start > 1 then s = mw.ustring.sub( s, start ) end local iterator = mw.ustring.gmatch(s, pattern) if match_index > 0 then -- Forward search for w in iterator do match_index = match_index - 1 if match_index == 0 then result = w break end end else -- Reverse search local result_table = {} local count = 1 for w in iterator do result_table[count] = w count = count + 1 end result = result_table[ count + match_index ] end end if result == nil then if nomatch == nil then return str._error( 'Match not found' ) else return nomatch end else return result end end --[[ match This function returns a substring from the source string that matches a specified pattern. Usage: {{#invoke:String|match|source_string|pattern_string|start_index|match_number|plain_flag|nomatch_output}} OR {{#invoke:String|match|s=source_string|pattern=pattern_string|start=start_index |match=match_number|plain=plain_flag|nomatch=nomatch_output}} Parameters s: The string to search pattern: The pattern or string to find within the string start: The index within the source string to start the search. The first character of the string has index 1. Defaults to 1. match: In some cases it may be possible to make multiple matches on a single string. This specifies which match to return, where the first match is match= 1. If a negative number is specified then a match is returned counting from the last match. Hence match = -1 is the same as requesting the last match. Defaults to 1. plain: A flag indicating that the pattern should be understood as plain text. Defaults to false. nomatch: If no match is found, output the "nomatch" value rather than an error. If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from each string. In some circumstances this is desirable, in other cases one may want to preserve the whitespace. If the match_number or start_index are out of range for the string being queried, then this function generates an error. An error is also generated if no match is found. If one adds the parameter ignore_errors=true, then the error will be suppressed and an empty string will be returned on any failure. For information on constructing Lua patterns, a form of [regular expression], see: * http://www.lua.org/manual/5.1/manual.html#5.4.1 * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns * http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns ]] -- This is the entry point for #invoke:String|match function str.match( frame ) local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} ) local s = new_args['s'] or '' local start = tonumber( new_args['start'] ) or 1 local plain_flag = str._getBoolean( new_args['plain'] or false ) local pattern = new_args['pattern'] or '' local match_index = math.floor( tonumber(new_args['match']) or 1 ) local nomatch = new_args['nomatch'] return str._match( s, pattern, start, match_index, plain_flag, nomatch ) end --[[ pos This function returns a single character from the target string at position pos. Usage: {{#invoke:String|pos|target_string|index_value}} OR {{#invoke:String|pos|target=target_string|pos=index_value}} Parameters target: The string to search pos: The index for the character to return If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from the target string. In some circumstances this is desirable, in other cases one may want to preserve the whitespace. The first character has an index value of 1. If one requests a negative value, this function will select a character by counting backwards from the end of the string. In other words pos = -1 is the same as asking for the last character. A requested value of zero, or a value greater than the length of the string returns an error. ]] function str.pos( frame ) local new_args = str._getParameters( frame.args, {'target', 'pos'} ) local target_str = new_args['target'] or '' local pos = tonumber( new_args['pos'] ) or 0 if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then return str._error( 'String index out of range' ) end return mw.ustring.sub( target_str, pos, pos ) end --[[ str_find This function duplicates the behavior of {{str_find}}, including all of its quirks. This is provided in order to support existing templates, but is NOT RECOMMENDED for new code and templates. New code is recommended to use the "find" function instead. Returns the first index in "source" that is a match to "target". Indexing is 1-based, and the function returns -1 if the "target" string is not present in "source". Important Note: If the "target" string is empty / missing, this function returns a value of "1", which is generally unexpected behavior, and must be accounted for separatetly. ]] function str.str_find( frame ) local new_args = str._getParameters( frame.args, {'source', 'target'} ) local source_str = new_args['source'] or '' local target_str = new_args['target'] or '' if target_str == '' then return 1 end local start = mw.ustring.find( source_str, target_str, 1, true ) if start == nil then start = -1 end return start end --[[ find This function allows one to search for a target string or pattern within another string. Usage: {{#invoke:String|find|source_str|target_string|start_index|plain_flag}} OR {{#invoke:String|find|source=source_str|target=target_str|start=start_index|plain=plain_flag}} Parameters source: The string to search target: The string or pattern to find within source start: The index within the source string to start the search, defaults to 1 plain: Boolean flag indicating that target should be understood as plain text and not as a Lua style regular expression, defaults to true If invoked using named parameters, Mediawiki will automatically remove any leading or trailing whitespace from the parameter. In some circumstances this is desirable, in other cases one may want to preserve the whitespace. This function returns the first index >= "start" where "target" can be found within "source". Indices are 1-based. If "target" is not found, then this function returns 0. If either "source" or "target" are missing / empty, this function also returns 0. This function should be safe for UTF-8 strings. ]] function str.find( frame ) local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } ) local source_str = new_args['source'] or '' local pattern = new_args['target'] or '' local start_pos = tonumber(new_args['start']) or 1 local plain = new_args['plain'] or true if source_str == '' or pattern == '' then return 0 end plain = str._getBoolean( plain ) local start = mw.ustring.find( source_str, pattern, start_pos, plain ) if start == nil then start = 0 end return start end --[[ replace This function allows one to replace a target string or pattern within another string. Usage: {{#invoke:String|replace|source_str|pattern_string|replace_string|replacement_count|plain_flag}} OR {{#invoke:String|replace|source=source_string|pattern=pattern_string|replace=replace_string| count=replacement_count|plain=plain_flag}} Parameters source: The string to search pattern: The string or pattern to find within source replace: The replacement text count: The number of occurences to replace, defaults to all. plain: Boolean flag indicating that pattern should be understood as plain text and not as a Lua style regular expression, defaults to true ]] function str.replace( frame ) local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } ) local source_str = new_args['source'] or '' local pattern = new_args['pattern'] or '' local replace = new_args['replace'] or '' local count = tonumber( new_args['count'] ) local plain = new_args['plain'] or true if source_str == '' or pattern == '' then return source_str end plain = str._getBoolean( plain ) if plain then pattern = str._escapePattern( pattern ) replace = mw.ustring.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences. end local result if count ~= nil then result = mw.ustring.gsub( source_str, pattern, replace, count ) else result = mw.ustring.gsub( source_str, pattern, replace ) end return result end --[[ simple function to pipe string.rep to templates. ]] function str.rep( frame ) local repetitions = tonumber( frame.args[2] ) if not repetitions then return str._error( 'function rep expects a number as second parameter, received "' .. ( frame.args[2] or '' ) .. '"' ) end return string.rep( frame.args[1] or '', repetitions ) end --[[ escapePattern This function escapes special characters from a Lua string pattern. See [1] for details on how patterns work. [1] https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns Usage: {{#invoke:String|escapePattern|pattern_string}} Parameters pattern_string: The pattern string to escape. ]] function str.escapePattern( frame ) local pattern_str = frame.args[1] if not pattern_str then return str._error( 'No pattern string specified' ) end local result = str._escapePattern( pattern_str ) return result end --[[ count This function counts the number of occurrences of one string in another. ]] function str.count(frame) local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'}) local source = args.source or '' local pattern = args.pattern or '' local plain = str._getBoolean(args.plain or true) if plain then pattern = str._escapePattern(pattern) end local _, count = mw.ustring.gsub(source, pattern, '') return count end --[[ endswith This function determines whether a string ends with another string. ]] function str.endswith(frame) local args = str._getParameters(frame.args, {'source', 'pattern'}) local source = args.source or '' local pattern = args.pattern or '' if pattern == '' then -- All strings end with the empty string. return "yes" end if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then return "yes" else return "" end end --[[ join Join all non empty arguments together; the first argument is the separator. Usage: {{#invoke:String|join|sep|one|two|three}} ]] function str.join(frame) local args = {} local sep for _, v in ipairs( frame.args ) do if sep then if v ~= '' then table.insert(args, v) end else sep = v end end return table.concat( args, sep or '' ) end --[[ Helper function that populates the argument list given that user may need to use a mix of named and unnamed parameters. This is relevant because named parameters are not identical to unnamed parameters due to string trimming, and when dealing with strings we sometimes want to either preserve or remove that whitespace depending on the application. ]] function str._getParameters( frame_args, arg_list ) local new_args = {} local index = 1 local value for _, arg in ipairs( arg_list ) do value = frame_args[arg] if value == nil then value = frame_args[index] index = index + 1 end new_args[arg] = value end return new_args end --[[ Helper function to handle error messages. ]] function str._error( error_str ) local frame = mw.getCurrentFrame() local error_category = frame.args.error_category or 'Errors reported by Module String' local ignore_errors = frame.args.ignore_errors or false local no_category = frame.args.no_category or false if str._getBoolean(ignore_errors) then return '' end local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>' if error_category ~= '' and not str._getBoolean( no_category ) then error_str = '[[Category:' .. error_category .. ']]' .. error_str end return error_str end --[[ Helper Function to interpret boolean strings ]] function str._getBoolean( boolean_str ) local boolean_value if type( boolean_str ) == 'string' then boolean_str = boolean_str:lower() if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0' or boolean_str == '' then boolean_value = false else boolean_value = true end elseif type( boolean_str ) == 'boolean' then boolean_value = boolean_str else error( 'No boolean value found' ) end return boolean_value end --[[ Helper function that escapes all pattern characters so that they will be treated as plain text. ]] function str._escapePattern( pattern_str ) return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ) end return str 6df794dd52434e0f6a372c9918f5a9dedd15f579 Module:List 828 352 699 698 2023-07-10T15:44:39Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:List]] Scribunto text/plain local libUtil = require('libraryUtil') local checkType = libUtil.checkType local mTableTools = require('Module:TableTools') local p = {} local listTypes = { ['bulleted'] = true, ['unbulleted'] = true, ['horizontal'] = true, ['ordered'] = true, ['horizontal_ordered'] = true } function p.makeListData(listType, args) -- Constructs a data table to be passed to p.renderList. local data = {} -- Classes and TemplateStyles data.classes = {} data.templatestyles = '' if listType == 'horizontal' or listType == 'horizontal_ordered' then table.insert(data.classes, 'hlist') data.templatestyles = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Hlist/styles.css' } } elseif listType == 'unbulleted' then table.insert(data.classes, 'plainlist') data.templatestyles = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Plainlist/styles.css' } } end table.insert(data.classes, args.class) -- Main div style data.style = args.style -- Indent for horizontal lists if listType == 'horizontal' or listType == 'horizontal_ordered' then local indent = tonumber(args.indent) indent = indent and indent * 1.6 or 0 if indent > 0 then data.marginLeft = indent .. 'em' end end -- List style types for ordered lists -- This could be "1, 2, 3", "a, b, c", or a number of others. The list style -- type is either set by the "type" attribute or the "list-style-type" CSS -- property. if listType == 'ordered' or listType == 'horizontal_ordered' then data.listStyleType = args.list_style_type or args['list-style-type'] data.type = args['type'] -- Detect invalid type attributes and attempt to convert them to -- list-style-type CSS properties. if data.type and not data.listStyleType and not tostring(data.type):find('^%s*[1AaIi]%s*$') then data.listStyleType = data.type data.type = nil end end -- List tag type if listType == 'ordered' or listType == 'horizontal_ordered' then data.listTag = 'ol' else data.listTag = 'ul' end -- Start number for ordered lists data.start = args.start if listType == 'horizontal_ordered' then -- Apply fix to get start numbers working with horizontal ordered lists. local startNum = tonumber(data.start) if startNum then data.counterReset = 'listitem ' .. tostring(startNum - 1) end end -- List style -- ul_style and ol_style are included for backwards compatibility. No -- distinction is made for ordered or unordered lists. data.listStyle = args.list_style -- List items -- li_style is included for backwards compatibility. item_style was included -- to be easier to understand for non-coders. data.itemStyle = args.item_style or args.li_style data.items = {} for _, num in ipairs(mTableTools.numKeys(args)) do local item = {} item.content = args[num] item.style = args['item' .. tostring(num) .. '_style'] or args['item_style' .. tostring(num)] item.value = args['item' .. tostring(num) .. '_value'] or args['item_value' .. tostring(num)] table.insert(data.items, item) end return data end function p.renderList(data) -- Renders the list HTML. -- Return the blank string if there are no list items. if type(data.items) ~= 'table' or #data.items < 1 then return '' end -- Render the main div tag. local root = mw.html.create('div') for _, class in ipairs(data.classes or {}) do root:addClass(class) end root:css{['margin-left'] = data.marginLeft} if data.style then root:cssText(data.style) end -- Render the list tag. local list = root:tag(data.listTag or 'ul') list :attr{start = data.start, type = data.type} :css{ ['counter-reset'] = data.counterReset, ['list-style-type'] = data.listStyleType } if data.listStyle then list:cssText(data.listStyle) end -- Render the list items for _, t in ipairs(data.items or {}) do local item = list:tag('li') if data.itemStyle then item:cssText(data.itemStyle) end if t.style then item:cssText(t.style) end item :attr{value = t.value} :wikitext(t.content) end return data.templatestyles .. tostring(root) end function p.renderTrackingCategories(args) local isDeprecated = false -- Tracks deprecated parameters. for k, v in pairs(args) do k = tostring(k) if k:find('^item_style%d+$') or k:find('^item_value%d+$') then isDeprecated = true break end end local ret = '' if isDeprecated then ret = ret .. '[[Category:List templates with deprecated parameters]]' end return ret end function p.makeList(listType, args) if not listType or not listTypes[listType] then error(string.format( "bad argument #1 to 'makeList' ('%s' is not a valid list type)", tostring(listType) ), 2) end checkType('makeList', 2, args, 'table') local data = p.makeListData(listType, args) local list = p.renderList(data) local trackingCategories = p.renderTrackingCategories(args) return list .. trackingCategories end for listType in pairs(listTypes) do p[listType] = function (frame) local mArguments = require('Module:Arguments') local origArgs = mArguments.getArgs(frame, { valueFunc = function (key, value) if not value or not mw.ustring.find(value, '%S') then return nil end if mw.ustring.find(value, '^%s*[%*#;:]') then return value else return value:match('^%s*(.-)%s*$') end return nil end }) -- Copy all the arguments to a new table, for faster indexing. local args = {} for k, v in pairs(origArgs) do args[k] = v end return p.makeList(listType, args) end end return p 7a4f36a6e9cd56370bdd8207d23694124821dc1a Module:TableTools 828 353 701 700 2023-07-10T15:44:40Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:TableTools]] Scribunto text/plain ------------------------------------------------------------------------------------ -- TableTools -- -- -- -- This module includes a number of functions for dealing with Lua tables. -- -- It is a meta-module, meant to be called from other Lua modules, and should not -- -- be called directly from #invoke. -- ------------------------------------------------------------------------------------ local libraryUtil = require('libraryUtil') local p = {} -- Define often-used variables and functions. local floor = math.floor local infinity = math.huge local checkType = libraryUtil.checkType local checkTypeMulti = libraryUtil.checkTypeMulti ------------------------------------------------------------------------------------ -- isPositiveInteger -- -- This function returns true if the given value is a positive integer, and false -- if not. Although it doesn't operate on tables, it is included here as it is -- useful for determining whether a given table key is in the array part or the -- hash part of a table. ------------------------------------------------------------------------------------ function p.isPositiveInteger(v) return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity end ------------------------------------------------------------------------------------ -- isNan -- -- This function returns true if the given number is a NaN value, and false if -- not. Although it doesn't operate on tables, it is included here as it is useful -- for determining whether a value can be a valid table key. Lua will generate an -- error if a NaN is used as a table key. ------------------------------------------------------------------------------------ function p.isNan(v) return type(v) == 'number' and v ~= v end ------------------------------------------------------------------------------------ -- shallowClone -- -- This returns a clone of a table. The value returned is a new table, but all -- subtables and functions are shared. Metamethods are respected, but the returned -- table will have no metatable of its own. ------------------------------------------------------------------------------------ function p.shallowClone(t) checkType('shallowClone', 1, t, 'table') local ret = {} for k, v in pairs(t) do ret[k] = v end return ret end ------------------------------------------------------------------------------------ -- removeDuplicates -- -- This removes duplicate values from an array. Non-positive-integer keys are -- ignored. The earliest value is kept, and all subsequent duplicate values are -- removed, but otherwise the array order is unchanged. ------------------------------------------------------------------------------------ function p.removeDuplicates(arr) checkType('removeDuplicates', 1, arr, 'table') local isNan = p.isNan local ret, exists = {}, {} for _, v in ipairs(arr) do if isNan(v) then -- NaNs can't be table keys, and they are also unique, so we don't need to check existence. ret[#ret + 1] = v else if not exists[v] then ret[#ret + 1] = v exists[v] = true end end end return ret end ------------------------------------------------------------------------------------ -- numKeys -- -- This takes a table and returns an array containing the numbers of any numerical -- keys that have non-nil values, sorted in numerical order. ------------------------------------------------------------------------------------ function p.numKeys(t) checkType('numKeys', 1, t, 'table') local isPositiveInteger = p.isPositiveInteger local nums = {} for k in pairs(t) do if isPositiveInteger(k) then nums[#nums + 1] = k end end table.sort(nums) return nums end ------------------------------------------------------------------------------------ -- affixNums -- -- This takes a table and returns an array containing the numbers of keys with the -- specified prefix and suffix. For example, for the table -- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will return -- {1, 3, 6}. ------------------------------------------------------------------------------------ function p.affixNums(t, prefix, suffix) checkType('affixNums', 1, t, 'table') checkType('affixNums', 2, prefix, 'string', true) checkType('affixNums', 3, suffix, 'string', true) local function cleanPattern(s) -- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally. return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1') end prefix = prefix or '' suffix = suffix or '' prefix = cleanPattern(prefix) suffix = cleanPattern(suffix) local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$' local nums = {} for k in pairs(t) do if type(k) == 'string' then local num = mw.ustring.match(k, pattern) if num then nums[#nums + 1] = tonumber(num) end end end table.sort(nums) return nums end ------------------------------------------------------------------------------------ -- numData -- -- Given a table with keys like {"foo1", "bar1", "foo2", "baz2"}, returns a table -- of subtables in the format -- {[1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'}}. -- Keys that don't end with an integer are stored in a subtable named "other". The -- compress option compresses the table so that it can be iterated over with -- ipairs. ------------------------------------------------------------------------------------ function p.numData(t, compress) checkType('numData', 1, t, 'table') checkType('numData', 2, compress, 'boolean', true) local ret = {} for k, v in pairs(t) do local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$') if num then num = tonumber(num) local subtable = ret[num] or {} if prefix == '' then -- Positional parameters match the blank string; put them at the start of the subtable instead. prefix = 1 end subtable[prefix] = v ret[num] = subtable else local subtable = ret.other or {} subtable[k] = v ret.other = subtable end end if compress then local other = ret.other ret = p.compressSparseArray(ret) ret.other = other end return ret end ------------------------------------------------------------------------------------ -- compressSparseArray -- -- This takes an array with one or more nil values, and removes the nil values -- while preserving the order, so that the array can be safely traversed with -- ipairs. ------------------------------------------------------------------------------------ function p.compressSparseArray(t) checkType('compressSparseArray', 1, t, 'table') local ret = {} local nums = p.numKeys(t) for _, num in ipairs(nums) do ret[#ret + 1] = t[num] end return ret end ------------------------------------------------------------------------------------ -- sparseIpairs -- -- This is an iterator for sparse arrays. It can be used like ipairs, but can -- handle nil values. ------------------------------------------------------------------------------------ function p.sparseIpairs(t) checkType('sparseIpairs', 1, t, 'table') local nums = p.numKeys(t) local i = 0 local lim = #nums return function () i = i + 1 if i <= lim then local key = nums[i] return key, t[key] else return nil, nil end end end ------------------------------------------------------------------------------------ -- size -- -- This returns the size of a key/value pair table. It will also work on arrays, -- but for arrays it is more efficient to use the # operator. ------------------------------------------------------------------------------------ function p.size(t) checkType('size', 1, t, 'table') local i = 0 for _ in pairs(t) do i = i + 1 end return i end local function defaultKeySort(item1, item2) -- "number" < "string", so numbers will be sorted before strings. local type1, type2 = type(item1), type(item2) if type1 ~= type2 then return type1 < type2 elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then return tostring(item1) < tostring(item2) else return item1 < item2 end end ------------------------------------------------------------------------------------ -- keysToList -- -- Returns an array of the keys in a table, sorted using either a default -- comparison function or a custom keySort function. ------------------------------------------------------------------------------------ function p.keysToList(t, keySort, checked) if not checked then checkType('keysToList', 1, t, 'table') checkTypeMulti('keysToList', 2, keySort, {'function', 'boolean', 'nil'}) end local arr = {} local index = 1 for k in pairs(t) do arr[index] = k index = index + 1 end if keySort ~= false then keySort = type(keySort) == 'function' and keySort or defaultKeySort table.sort(arr, keySort) end return arr end ------------------------------------------------------------------------------------ -- sortedPairs -- -- Iterates through a table, with the keys sorted using the keysToList function. -- If there are only numerical keys, sparseIpairs is probably more efficient. ------------------------------------------------------------------------------------ function p.sortedPairs(t, keySort) checkType('sortedPairs', 1, t, 'table') checkType('sortedPairs', 2, keySort, 'function', true) local arr = p.keysToList(t, keySort, true) local i = 0 return function () i = i + 1 local key = arr[i] if key ~= nil then return key, t[key] else return nil, nil end end end ------------------------------------------------------------------------------------ -- isArray -- -- Returns true if the given value is a table and all keys are consecutive -- integers starting at 1. ------------------------------------------------------------------------------------ function p.isArray(v) if type(v) ~= 'table' then return false end local i = 0 for _ in pairs(v) do i = i + 1 if v[i] == nil then return false end end return true end ------------------------------------------------------------------------------------ -- isArrayLike -- -- Returns true if the given value is iterable and all keys are consecutive -- integers starting at 1. ------------------------------------------------------------------------------------ function p.isArrayLike(v) if not pcall(pairs, v) then return false end local i = 0 for _ in pairs(v) do i = i + 1 if v[i] == nil then return false end end return true end ------------------------------------------------------------------------------------ -- invert -- -- Transposes the keys and values in an array. For example, {"a", "b", "c"} -> -- {a = 1, b = 2, c = 3}. Duplicates are not supported (result values refer to -- the index of the last duplicate) and NaN values are ignored. ------------------------------------------------------------------------------------ function p.invert(arr) checkType("invert", 1, arr, "table") local isNan = p.isNan local map = {} for i, v in ipairs(arr) do if not isNan(v) then map[v] = i end end return map end ------------------------------------------------------------------------------------ -- listToSet -- -- Creates a set from the array part of the table. Indexing the set by any of the -- values of the array returns true. For example, {"a", "b", "c"} -> -- {a = true, b = true, c = true}. NaN values are ignored as Lua considers them -- never equal to any value (including other NaNs or even themselves). ------------------------------------------------------------------------------------ function p.listToSet(arr) checkType("listToSet", 1, arr, "table") local isNan = p.isNan local set = {} for _, v in ipairs(arr) do if not isNan(v) then set[v] = true end end return set end ------------------------------------------------------------------------------------ -- deepCopy -- -- Recursive deep copy function. Preserves identities of subtables. ------------------------------------------------------------------------------------ local function _deepCopy(orig, includeMetatable, already_seen) -- Stores copies of tables indexed by the original table. already_seen = already_seen or {} local copy = already_seen[orig] if copy ~= nil then return copy end if type(orig) == 'table' then copy = {} for orig_key, orig_value in pairs(orig) do copy[_deepCopy(orig_key, includeMetatable, already_seen)] = _deepCopy(orig_value, includeMetatable, already_seen) end already_seen[orig] = copy if includeMetatable then local mt = getmetatable(orig) if mt ~= nil then local mt_copy = _deepCopy(mt, includeMetatable, already_seen) setmetatable(copy, mt_copy) already_seen[mt] = mt_copy end end else -- number, string, boolean, etc copy = orig end return copy end function p.deepCopy(orig, noMetatable, already_seen) checkType("deepCopy", 3, already_seen, "table", true) return _deepCopy(orig, not noMetatable, already_seen) end ------------------------------------------------------------------------------------ -- sparseConcat -- -- Concatenates all values in the table that are indexed by a number, in order. -- sparseConcat{a, nil, c, d} => "acd" -- sparseConcat{nil, b, c, d} => "bcd" ------------------------------------------------------------------------------------ function p.sparseConcat(t, sep, i, j) local arr = {} local arr_i = 0 for _, v in p.sparseIpairs(t) do arr_i = arr_i + 1 arr[arr_i] = v end return table.concat(arr, sep, i, j) end ------------------------------------------------------------------------------------ -- length -- -- Finds the length of an array, or of a quasi-array with keys such as "data1", -- "data2", etc., using an exponential search algorithm. It is similar to the -- operator #, but may return a different value when there are gaps in the array -- portion of the table. Intended to be used on data loaded with mw.loadData. For -- other tables, use #. -- Note: #frame.args in frame object always be set to 0, regardless of the number -- of unnamed template parameters, so use this function for frame.args. ------------------------------------------------------------------------------------ function p.length(t, prefix) -- requiring module inline so that [[Module:Exponential search]] which is -- only needed by this one function doesn't get millions of transclusions local expSearch = require("Module:Exponential search") checkType('length', 1, t, 'table') checkType('length', 2, prefix, 'string', true) return expSearch(function (i) local key if prefix then key = prefix .. tostring(i) else key = i end return t[key] ~= nil end) or 0 end ------------------------------------------------------------------------------------ -- inArray -- -- Returns true if valueToFind is a member of the array, and false otherwise. ------------------------------------------------------------------------------------ function p.inArray(arr, valueToFind) checkType("inArray", 1, arr, "table") -- if valueToFind is nil, error? for _, v in ipairs(arr) do if v == valueToFind then return true end end return false end return p 085e7094ac84eb0132ee65822cf3f69cd8ba3d81 Module:Effective protection expiry 828 354 703 702 2023-07-10T15:44:40Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Effective_protection_expiry]] Scribunto text/plain local p = {} -- Returns the expiry of a restriction of an action on a given title, or unknown if it cannot be known. -- If no title is specified, the title of the page being displayed is used. function p._main(action, pagename) local title if type(pagename) == 'table' and pagename.prefixedText then title = pagename elseif pagename then title = mw.title.new(pagename) else title = mw.title.getCurrentTitle() end pagename = title.prefixedText if action == 'autoreview' then local stabilitySettings = mw.ext.FlaggedRevs.getStabilitySettings(title) return stabilitySettings and stabilitySettings.expiry or 'unknown' elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' then error( 'First parameter must be one of edit, move, create, upload, autoreview', 2 ) end local rawExpiry = mw.getCurrentFrame():callParserFunction('PROTECTIONEXPIRY', action, pagename) if rawExpiry == 'infinity' then return 'infinity' elseif rawExpiry == '' then return 'unknown' else local year, month, day, hour, minute, second = rawExpiry:match( '^(%d%d%d%d)(%d%d)(%d%d)(%d%d)(%d%d)(%d%d)$' ) if year then return string.format( '%s-%s-%sT%s:%s:%s', year, month, day, hour, minute, second ) else error('internal error in Module:Effective protection expiry; malformed expiry timestamp') end end end setmetatable(p, { __index = function(t, k) return function(frame) return t._main(k, frame.args[1]) end end }) return p 9a8c58dc2667232ed08a9b206a5d89ca8150312b Module:Effective protection level 828 355 705 704 2023-07-10T15:44:40Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Effective_protection_level]] Scribunto text/plain local p = {} -- Returns the permission required to perform a given action on a given title. -- If no title is specified, the title of the page being displayed is used. function p._main(action, pagename) local title if type(pagename) == 'table' and pagename.prefixedText then title = pagename elseif pagename then title = mw.title.new(pagename) else title = mw.title.getCurrentTitle() end pagename = title.prefixedText if action == 'autoreview' then local level = mw.ext.FlaggedRevs.getStabilitySettings(title) level = level and level.autoreview if level == 'review' then return 'reviewer' elseif level ~= '' then return level else return nil -- not '*'. a page not being PC-protected is distinct from it being PC-protected with anyone able to review. also not '', as that would mean PC-protected but nobody can review end elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' and action ~= 'undelete' then error( 'First parameter must be one of edit, move, create, upload, undelete, autoreview', 2 ) end if title.namespace == 8 then -- MediaWiki namespace if title.text:sub(-3) == '.js' or title.text:sub(-4) == '.css' or title.contentModel == 'javascript' or title.contentModel == 'css' then -- site JS or CSS page return 'interfaceadmin' else -- any non-JS/CSS MediaWiki page return 'sysop' end elseif title.namespace == 2 and title.isSubpage then if title.contentModel == 'javascript' or title.contentModel == 'css' then -- user JS or CSS page return 'interfaceadmin' elseif title.contentModel == 'json' then -- user JSON page return 'sysop' end end if action == 'undelete' then return 'sysop' end local level = title.protectionLevels[action] and title.protectionLevels[action][1] if level == 'sysop' or level == 'editprotected' then return 'sysop' elseif title.cascadingProtection.restrictions[action] and title.cascadingProtection.restrictions[action][1] then -- used by a cascading-protected page return 'sysop' elseif level == 'templateeditor' then return 'templateeditor' elseif action == 'move' then local blacklistentry = mw.ext.TitleBlacklist.test('edit', pagename) -- Testing action edit is correct, since this is for the source page. The target page name gets tested with action move. if blacklistentry and not blacklistentry.params.autoconfirmed then return 'templateeditor' elseif title.namespace == 6 then return 'filemover' elseif level == 'extendedconfirmed' then return 'extendedconfirmed' else return 'autoconfirmed' end end local blacklistentry = mw.ext.TitleBlacklist.test(action, pagename) if blacklistentry then if not blacklistentry.params.autoconfirmed then return 'templateeditor' elseif level == 'extendedconfirmed' then return 'extendedconfirmed' else return 'autoconfirmed' end elseif level == 'editsemiprotected' then -- create-semiprotected pages return this for some reason return 'autoconfirmed' elseif level then return level elseif action == 'upload' then return 'autoconfirmed' elseif action == 'create' and title.namespace % 2 == 0 and title.namespace ~= 118 then -- You need to be registered, but not autoconfirmed, to create non-talk pages other than drafts return 'user' else return '*' end end setmetatable(p, { __index = function(t, k) return function(frame) return t._main(k, frame.args[1]) end end }) return p 70256a489edf6be9808031b14a7e3ef3e025da97 Module:File link 828 356 707 706 2023-07-10T15:44:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:File_link]] Scribunto text/plain -- This module provides a library for formatting file wikilinks. local yesno = require('Module:Yesno') local checkType = require('libraryUtil').checkType local p = {} function p._main(args) checkType('_main', 1, args, 'table') -- This is basically libraryUtil.checkTypeForNamedArg, but we are rolling our -- own function to get the right error level. local function checkArg(key, val, level) if type(val) ~= 'string' then error(string.format( "type error in '%s' parameter of '_main' (expected string, got %s)", key, type(val) ), level) end end local ret = {} -- Adds a positional parameter to the buffer. local function addPositional(key) local val = args[key] if not val then return nil end checkArg(key, val, 4) ret[#ret + 1] = val end -- Adds a named parameter to the buffer. We assume that the parameter name -- is the same as the argument key. local function addNamed(key) local val = args[key] if not val then return nil end checkArg(key, val, 4) ret[#ret + 1] = key .. '=' .. val end -- Filename checkArg('file', args.file, 3) ret[#ret + 1] = 'File:' .. args.file -- Format if args.format then checkArg('format', args.format) if args.formatfile then checkArg('formatfile', args.formatfile) ret[#ret + 1] = args.format .. '=' .. args.formatfile else ret[#ret + 1] = args.format end end -- Border if yesno(args.border) then ret[#ret + 1] = 'border' end addPositional('location') addPositional('alignment') addPositional('size') addNamed('upright') addNamed('link') addNamed('alt') addNamed('page') addNamed('class') addNamed('lang') addNamed('start') addNamed('end') addNamed('thumbtime') addPositional('caption') return string.format('[[%s]]', table.concat(ret, '|')) end function p.main(frame) local origArgs = require('Module:Arguments').getArgs(frame, { wrappers = 'Template:File link' }) if not origArgs.file then error("'file' parameter missing from [[Template:File link]]", 0) end -- Copy the arguments that were passed to a new table to avoid looking up -- every possible parameter in the frame object. local args = {} for k, v in pairs(origArgs) do -- Make _BLANK a special argument to add a blank parameter. For use in -- conditional templates etc. it is useful for blank arguments to be -- ignored, but we still need a way to specify them so that we can do -- things like [[File:Example.png|link=]]. if v == '_BLANK' then v = '' end args[k] = v end return p._main(args) end return p 66925f088d11530f2482f04181a3baaaa0ad3d0c Module:Protection banner 828 357 709 708 2023-07-10T15:44:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Protection_banner]] Scribunto text/plain -- This module implements {{pp-meta}} and its daughter templates such as -- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}. -- Initialise necessary modules. require('strict') local makeFileLink = require('Module:File link')._main local effectiveProtectionLevel = require('Module:Effective protection level')._main local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main local yesno = require('Module:Yesno') -- Lazily initialise modules and objects we don't always need. local getArgs, makeMessageBox, lang -- Set constants. local CONFIG_MODULE = 'Module:Protection banner/config' -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function makeCategoryLink(cat, sort) if cat then return string.format( '[[%s:%s|%s]]', mw.site.namespaces[14].name, cat, sort ) end end -- Validation function for the expiry and the protection date local function validateDate(dateString, dateType) if not lang then lang = mw.language.getContentLanguage() end local success, result = pcall(lang.formatDate, lang, 'U', dateString) if success then result = tonumber(result) if result then return result end end error(string.format( 'invalid %s: %s', dateType, tostring(dateString) ), 4) end local function makeFullUrl(page, query, display) return string.format( '[%s %s]', tostring(mw.uri.fullUrl(page, query)), display ) end -- Given a directed graph formatted as node -> table of direct successors, -- get a table of all nodes reachable from a given node (though always -- including the given node). local function getReachableNodes(graph, start) local toWalk, retval = {[start] = true}, {} while true do -- Can't use pairs() since we're adding and removing things as we're iterating local k = next(toWalk) -- This always gets the "first" key if k == nil then return retval end toWalk[k] = nil retval[k] = true for _,v in ipairs(graph[k]) do if not retval[v] then toWalk[v] = true end end end end -------------------------------------------------------------------------------- -- Protection class -------------------------------------------------------------------------------- local Protection = {} Protection.__index = Protection Protection.supportedActions = { edit = true, move = true, autoreview = true, upload = true } Protection.bannerConfigFields = { 'text', 'explanation', 'tooltip', 'alt', 'link', 'image' } function Protection.new(args, cfg, title) local obj = {} obj._cfg = cfg obj.title = title or mw.title.getCurrentTitle() -- Set action if not args.action then obj.action = 'edit' elseif Protection.supportedActions[args.action] then obj.action = args.action else error(string.format( 'invalid action: %s', tostring(args.action) ), 3) end -- Set level obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title) if not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then -- Users need to be autoconfirmed to move pages anyway, so treat -- semi-move-protected pages as unprotected. obj.level = '*' end -- Set expiry local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title) if effectiveExpiry == 'infinity' then obj.expiry = 'indef' elseif effectiveExpiry ~= 'unknown' then obj.expiry = validateDate(effectiveExpiry, 'expiry date') end -- Set reason if args[1] then obj.reason = mw.ustring.lower(args[1]) if obj.reason:find('|') then error('reasons cannot contain the pipe character ("|")', 3) end end -- Set protection date if args.date then obj.protectionDate = validateDate(args.date, 'protection date') end -- Set banner config do obj.bannerConfig = {} local configTables = {} if cfg.banners[obj.action] then configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason] end if cfg.defaultBanners[obj.action] then configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level] configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default end configTables[#configTables + 1] = cfg.masterBanner for i, field in ipairs(Protection.bannerConfigFields) do for j, t in ipairs(configTables) do if t[field] then obj.bannerConfig[field] = t[field] break end end end end return setmetatable(obj, Protection) end function Protection:isUserScript() -- Whether the page is a user JavaScript or CSS page. local title = self.title return title.namespace == 2 and ( title.contentModel == 'javascript' or title.contentModel == 'css' ) end function Protection:isProtected() return self.level ~= '*' end function Protection:shouldShowLock() -- Whether we should output a banner/padlock return self:isProtected() and not self:isUserScript() end -- Whether this page needs a protection category. Protection.shouldHaveProtectionCategory = Protection.shouldShowLock function Protection:isTemporary() return type(self.expiry) == 'number' end function Protection:makeProtectionCategory() if not self:shouldHaveProtectionCategory() then return '' end local cfg = self._cfg local title = self.title -- Get the expiry key fragment. local expiryFragment if self.expiry == 'indef' then expiryFragment = self.expiry elseif type(self.expiry) == 'number' then expiryFragment = 'temp' end -- Get the namespace key fragment. local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace] if not namespaceFragment and title.namespace % 2 == 1 then namespaceFragment = 'talk' end -- Define the order that key fragments are tested in. This is done with an -- array of tables containing the value to be tested, along with its -- position in the cfg.protectionCategories table. local order = { {val = expiryFragment, keypos = 1}, {val = namespaceFragment, keypos = 2}, {val = self.reason, keypos = 3}, {val = self.level, keypos = 4}, {val = self.action, keypos = 5} } --[[ -- The old protection templates used an ad-hoc protection category system, -- with some templates prioritising namespaces in their categories, and -- others prioritising the protection reason. To emulate this in this module -- we use the config table cfg.reasonsWithNamespacePriority to set the -- reasons for which namespaces have priority over protection reason. -- If we are dealing with one of those reasons, move the namespace table to -- the end of the order table, i.e. give it highest priority. If not, the -- reason should have highest priority, so move that to the end of the table -- instead. --]] table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3)) --[[ -- Define the attempt order. Inactive subtables (subtables with nil "value" -- fields) are moved to the end, where they will later be given the key -- "all". This is to cut down on the number of table lookups in -- cfg.protectionCategories, which grows exponentially with the number of -- non-nil keys. We keep track of the number of active subtables with the -- noActive parameter. --]] local noActive, attemptOrder do local active, inactive = {}, {} for i, t in ipairs(order) do if t.val then active[#active + 1] = t else inactive[#inactive + 1] = t end end noActive = #active attemptOrder = active for i, t in ipairs(inactive) do attemptOrder[#attemptOrder + 1] = t end end --[[ -- Check increasingly generic key combinations until we find a match. If a -- specific category exists for the combination of key fragments we are -- given, that match will be found first. If not, we keep trying different -- key fragment combinations until we match using the key -- "all-all-all-all-all". -- -- To generate the keys, we index the key subtables using a binary matrix -- with indexes i and j. j is only calculated up to the number of active -- subtables. For example, if there were three active subtables, the matrix -- would look like this, with 0 corresponding to the key fragment "all", and -- 1 corresponding to other key fragments. -- -- j 1 2 3 -- i -- 1 1 1 1 -- 2 0 1 1 -- 3 1 0 1 -- 4 0 0 1 -- 5 1 1 0 -- 6 0 1 0 -- 7 1 0 0 -- 8 0 0 0 -- -- Values of j higher than the number of active subtables are set -- to the string "all". -- -- A key for cfg.protectionCategories is constructed for each value of i. -- The position of the value in the key is determined by the keypos field in -- each subtable. --]] local cats = cfg.protectionCategories for i = 1, 2^noActive do local key = {} for j, t in ipairs(attemptOrder) do if j > noActive then key[t.keypos] = 'all' else local quotient = i / 2 ^ (j - 1) quotient = math.ceil(quotient) if quotient % 2 == 1 then key[t.keypos] = t.val else key[t.keypos] = 'all' end end end key = table.concat(key, '|') local attempt = cats[key] if attempt then return makeCategoryLink(attempt, title.text) end end return '' end function Protection:isIncorrect() local expiry = self.expiry return not self:shouldHaveProtectionCategory() or type(expiry) == 'number' and expiry < os.time() end function Protection:isTemplateProtectedNonTemplate() local action, namespace = self.action, self.title.namespace return self.level == 'templateeditor' and ( (action ~= 'edit' and action ~= 'move') or (namespace ~= 10 and namespace ~= 828) ) end function Protection:makeCategoryLinks() local msg = self._cfg.msg local ret = {self:makeProtectionCategory()} if self:isIncorrect() then ret[#ret + 1] = makeCategoryLink( msg['tracking-category-incorrect'], self.title.text ) end if self:isTemplateProtectedNonTemplate() then ret[#ret + 1] = makeCategoryLink( msg['tracking-category-template'], self.title.text ) end return table.concat(ret) end -------------------------------------------------------------------------------- -- Blurb class -------------------------------------------------------------------------------- local Blurb = {} Blurb.__index = Blurb Blurb.bannerTextFields = { text = true, explanation = true, tooltip = true, alt = true, link = true } function Blurb.new(protectionObj, args, cfg) return setmetatable({ _cfg = cfg, _protectionObj = protectionObj, _args = args }, Blurb) end -- Private methods -- function Blurb:_formatDate(num) -- Formats a Unix timestamp into dd Month, YYYY format. lang = lang or mw.language.getContentLanguage() local success, date = pcall( lang.formatDate, lang, self._cfg.msg['expiry-date-format'] or 'j F Y', '@' .. tostring(num) ) if success then return date end end function Blurb:_getExpandedMessage(msgKey) return self:_substituteParameters(self._cfg.msg[msgKey]) end function Blurb:_substituteParameters(msg) if not self._params then local parameterFuncs = {} parameterFuncs.CURRENTVERSION = self._makeCurrentVersionParameter parameterFuncs.EDITREQUEST = self._makeEditRequestParameter parameterFuncs.EXPIRY = self._makeExpiryParameter parameterFuncs.EXPLANATIONBLURB = self._makeExplanationBlurbParameter parameterFuncs.IMAGELINK = self._makeImageLinkParameter parameterFuncs.INTROBLURB = self._makeIntroBlurbParameter parameterFuncs.INTROFRAGMENT = self._makeIntroFragmentParameter parameterFuncs.PAGETYPE = self._makePagetypeParameter parameterFuncs.PROTECTIONBLURB = self._makeProtectionBlurbParameter parameterFuncs.PROTECTIONDATE = self._makeProtectionDateParameter parameterFuncs.PROTECTIONLEVEL = self._makeProtectionLevelParameter parameterFuncs.PROTECTIONLOG = self._makeProtectionLogParameter parameterFuncs.TALKPAGE = self._makeTalkPageParameter parameterFuncs.TOOLTIPBLURB = self._makeTooltipBlurbParameter parameterFuncs.TOOLTIPFRAGMENT = self._makeTooltipFragmentParameter parameterFuncs.VANDAL = self._makeVandalTemplateParameter self._params = setmetatable({}, { __index = function (t, k) local param if parameterFuncs[k] then param = parameterFuncs[k](self) end param = param or '' t[k] = param return param end }) end msg = msg:gsub('${(%u+)}', self._params) return msg end function Blurb:_makeCurrentVersionParameter() -- A link to the page history or the move log, depending on the kind of -- protection. local pagename = self._protectionObj.title.prefixedText if self._protectionObj.action == 'move' then -- We need the move log link. return makeFullUrl( 'Special:Log', {type = 'move', page = pagename}, self:_getExpandedMessage('current-version-move-display') ) else -- We need the history link. return makeFullUrl( pagename, {action = 'history'}, self:_getExpandedMessage('current-version-edit-display') ) end end function Blurb:_makeEditRequestParameter() local mEditRequest = require('Module:Submit an edit request') local action = self._protectionObj.action local level = self._protectionObj.level -- Get the edit request type. local requestType if action == 'edit' then if level == 'autoconfirmed' then requestType = 'semi' elseif level == 'extendedconfirmed' then requestType = 'extended' elseif level == 'templateeditor' then requestType = 'template' end end requestType = requestType or 'full' -- Get the display value. local display = self:_getExpandedMessage('edit-request-display') return mEditRequest._link{type = requestType, display = display} end function Blurb:_makeExpiryParameter() local expiry = self._protectionObj.expiry if type(expiry) == 'number' then return self:_formatDate(expiry) else return expiry end end function Blurb:_makeExplanationBlurbParameter() -- Cover special cases first. if self._protectionObj.title.namespace == 8 then -- MediaWiki namespace return self:_getExpandedMessage('explanation-blurb-nounprotect') end -- Get explanation blurb table keys local action = self._protectionObj.action local level = self._protectionObj.level local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject' -- Find the message in the explanation blurb table and substitute any -- parameters. local explanations = self._cfg.explanationBlurbs local msg if explanations[action][level] and explanations[action][level][talkKey] then msg = explanations[action][level][talkKey] elseif explanations[action][level] and explanations[action][level].default then msg = explanations[action][level].default elseif explanations[action].default and explanations[action].default[talkKey] then msg = explanations[action].default[talkKey] elseif explanations[action].default and explanations[action].default.default then msg = explanations[action].default.default else error(string.format( 'could not find explanation blurb for action "%s", level "%s" and talk key "%s"', action, level, talkKey ), 8) end return self:_substituteParameters(msg) end function Blurb:_makeImageLinkParameter() local imageLinks = self._cfg.imageLinks local action = self._protectionObj.action local level = self._protectionObj.level local msg if imageLinks[action][level] then msg = imageLinks[action][level] elseif imageLinks[action].default then msg = imageLinks[action].default else msg = imageLinks.edit.default end return self:_substituteParameters(msg) end function Blurb:_makeIntroBlurbParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('intro-blurb-expiry') else return self:_getExpandedMessage('intro-blurb-noexpiry') end end function Blurb:_makeIntroFragmentParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('intro-fragment-expiry') else return self:_getExpandedMessage('intro-fragment-noexpiry') end end function Blurb:_makePagetypeParameter() local pagetypes = self._cfg.pagetypes return pagetypes[self._protectionObj.title.namespace] or pagetypes.default or error('no default pagetype defined', 8) end function Blurb:_makeProtectionBlurbParameter() local protectionBlurbs = self._cfg.protectionBlurbs local action = self._protectionObj.action local level = self._protectionObj.level local msg if protectionBlurbs[action][level] then msg = protectionBlurbs[action][level] elseif protectionBlurbs[action].default then msg = protectionBlurbs[action].default elseif protectionBlurbs.edit.default then msg = protectionBlurbs.edit.default else error('no protection blurb defined for protectionBlurbs.edit.default', 8) end return self:_substituteParameters(msg) end function Blurb:_makeProtectionDateParameter() local protectionDate = self._protectionObj.protectionDate if type(protectionDate) == 'number' then return self:_formatDate(protectionDate) else return protectionDate end end function Blurb:_makeProtectionLevelParameter() local protectionLevels = self._cfg.protectionLevels local action = self._protectionObj.action local level = self._protectionObj.level local msg if protectionLevels[action][level] then msg = protectionLevels[action][level] elseif protectionLevels[action].default then msg = protectionLevels[action].default elseif protectionLevels.edit.default then msg = protectionLevels.edit.default else error('no protection level defined for protectionLevels.edit.default', 8) end return self:_substituteParameters(msg) end function Blurb:_makeProtectionLogParameter() local pagename = self._protectionObj.title.prefixedText if self._protectionObj.action == 'autoreview' then -- We need the pending changes log. return makeFullUrl( 'Special:Log', {type = 'stable', page = pagename}, self:_getExpandedMessage('pc-log-display') ) else -- We need the protection log. return makeFullUrl( 'Special:Log', {type = 'protect', page = pagename}, self:_getExpandedMessage('protection-log-display') ) end end function Blurb:_makeTalkPageParameter() return string.format( '[[%s:%s#%s|%s]]', mw.site.namespaces[self._protectionObj.title.namespace].talk.name, self._protectionObj.title.text, self._args.section or 'top', self:_getExpandedMessage('talk-page-link-display') ) end function Blurb:_makeTooltipBlurbParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('tooltip-blurb-expiry') else return self:_getExpandedMessage('tooltip-blurb-noexpiry') end end function Blurb:_makeTooltipFragmentParameter() if self._protectionObj:isTemporary() then return self:_getExpandedMessage('tooltip-fragment-expiry') else return self:_getExpandedMessage('tooltip-fragment-noexpiry') end end function Blurb:_makeVandalTemplateParameter() return mw.getCurrentFrame():expandTemplate{ title="vandal-m", args={self._args.user or self._protectionObj.title.baseText} } end -- Public methods -- function Blurb:makeBannerText(key) -- Validate input. if not key or not Blurb.bannerTextFields[key] then error(string.format( '"%s" is not a valid banner config field', tostring(key) ), 2) end -- Generate the text. local msg = self._protectionObj.bannerConfig[key] if type(msg) == 'string' then return self:_substituteParameters(msg) elseif type(msg) == 'function' then msg = msg(self._protectionObj, self._args) if type(msg) ~= 'string' then error(string.format( 'bad output from banner config function with key "%s"' .. ' (expected string, got %s)', tostring(key), type(msg) ), 4) end return self:_substituteParameters(msg) end end -------------------------------------------------------------------------------- -- BannerTemplate class -------------------------------------------------------------------------------- local BannerTemplate = {} BannerTemplate.__index = BannerTemplate function BannerTemplate.new(protectionObj, cfg) local obj = {} obj._cfg = cfg -- Set the image filename. local imageFilename = protectionObj.bannerConfig.image if imageFilename then obj._imageFilename = imageFilename else -- If an image filename isn't specified explicitly in the banner config, -- generate it from the protection status and the namespace. local action = protectionObj.action local level = protectionObj.level local namespace = protectionObj.title.namespace local reason = protectionObj.reason -- Deal with special cases first. if ( namespace == 10 or namespace == 828 or reason and obj._cfg.indefImageReasons[reason] ) and action == 'edit' and level == 'sysop' and not protectionObj:isTemporary() then -- Fully protected modules and templates get the special red "indef" -- padlock. obj._imageFilename = obj._cfg.msg['image-filename-indef'] else -- Deal with regular protection types. local images = obj._cfg.images if images[action] then if images[action][level] then obj._imageFilename = images[action][level] elseif images[action].default then obj._imageFilename = images[action].default end end end end return setmetatable(obj, BannerTemplate) end function BannerTemplate:renderImage() local filename = self._imageFilename or self._cfg.msg['image-filename-default'] or 'Transparent.gif' return makeFileLink{ file = filename, size = (self.imageWidth or 20) .. 'px', alt = self._imageAlt, link = self._imageLink, caption = self.imageCaption } end -------------------------------------------------------------------------------- -- Banner class -------------------------------------------------------------------------------- local Banner = setmetatable({}, BannerTemplate) Banner.__index = Banner function Banner.new(protectionObj, blurbObj, cfg) local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. obj.imageWidth = 40 obj.imageCaption = blurbObj:makeBannerText('alt') -- Large banners use the alt text for the tooltip. obj._reasonText = blurbObj:makeBannerText('text') obj._explanationText = blurbObj:makeBannerText('explanation') obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing. return setmetatable(obj, Banner) end function Banner:__tostring() -- Renders the banner. makeMessageBox = makeMessageBox or require('Module:Message box').main local reasonText = self._reasonText or error('no reason text set', 2) local explanationText = self._explanationText local mbargs = { page = self._page, type = 'protection', image = self:renderImage(), text = string.format( "'''%s'''%s", reasonText, explanationText and '<br />' .. explanationText or '' ) } return makeMessageBox('mbox', mbargs) end -------------------------------------------------------------------------------- -- Padlock class -------------------------------------------------------------------------------- local Padlock = setmetatable({}, BannerTemplate) Padlock.__index = Padlock function Padlock.new(protectionObj, blurbObj, cfg) local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. obj.imageWidth = 20 obj.imageCaption = blurbObj:makeBannerText('tooltip') obj._imageAlt = blurbObj:makeBannerText('alt') obj._imageLink = blurbObj:makeBannerText('link') obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action] or cfg.padlockIndicatorNames.default or 'pp-default' return setmetatable(obj, Padlock) end function Padlock:__tostring() local frame = mw.getCurrentFrame() -- The nowiki tag helps prevent whitespace at the top of articles. return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{ name = 'indicator', args = {name = self._indicatorName}, content = self:renderImage() } end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p = {} function p._exportClasses() -- This is used for testing purposes. return { Protection = Protection, Blurb = Blurb, BannerTemplate = BannerTemplate, Banner = Banner, Padlock = Padlock, } end function p._main(args, cfg, title) args = args or {} cfg = cfg or require(CONFIG_MODULE) local protectionObj = Protection.new(args, cfg, title) local ret = {} -- If a page's edit protection is equally or more restrictive than its -- protection from some other action, then don't bother displaying anything -- for the other action (except categories). if not yesno(args.catonly) and (protectionObj.action == 'edit' or args.demolevel or not getReachableNodes( cfg.hierarchy, protectionObj.level )[effectiveProtectionLevel('edit', protectionObj.title)]) then -- Initialise the blurb object local blurbObj = Blurb.new(protectionObj, args, cfg) -- Render the banner if protectionObj:shouldShowLock() then ret[#ret + 1] = tostring( (yesno(args.small) and Padlock or Banner) .new(protectionObj, blurbObj, cfg) ) end end -- Render the categories if yesno(args.category) ~= false then ret[#ret + 1] = protectionObj:makeCategoryLinks() end return table.concat(ret) end function p.main(frame, cfg) cfg = cfg or require(CONFIG_MODULE) -- Find default args, if any. local parent = frame.getParent and frame:getParent() local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')] -- Find user args, and use the parent frame if we are being called from a -- wrapper template. getArgs = getArgs or require('Module:Arguments').getArgs local userArgs = getArgs(frame, { parentOnly = defaultArgs, frameOnly = not defaultArgs }) -- Build the args table. User-specified args overwrite default args. local args = {} for k, v in pairs(defaultArgs or {}) do args[k] = v end for k, v in pairs(userArgs) do args[k] = v end return p._main(args, cfg) end return p 894f0884d4c2da1ce19d385b96f59af654b0946a Module:Protection banner/config 828 358 711 710 2023-07-10T15:44:41Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Protection_banner/config]] Scribunto text/plain -- This module provides configuration data for [[Module:Protection banner]]. return { -------------------------------------------------------------------------------- -- -- BANNER DATA -- -------------------------------------------------------------------------------- --[[ -- Banner data consists of six fields: -- * text - the main protection text that appears at the top of protection -- banners. -- * explanation - the text that appears below the main protection text, used -- to explain the details of the protection. -- * tooltip - the tooltip text you see when you move the mouse over a small -- padlock icon. -- * link - the page that the small padlock icon links to. -- * alt - the alt text for the small padlock icon. This is also used as tooltip -- text for the large protection banners. -- * image - the padlock image used in both protection banners and small padlock -- icons. -- -- The module checks in three separate tables to find a value for each field. -- First it checks the banners table, which has values specific to the reason -- for the page being protected. Then the module checks the defaultBanners -- table, which has values specific to each protection level. Finally, the -- module checks the masterBanner table, which holds data for protection -- templates to use if no data has been found in the previous two tables. -- -- The values in the banner data can take parameters. These are specified -- using ${TEXTLIKETHIS} (a dollar sign preceding a parameter name -- enclosed in curly braces). -- -- Available parameters: -- -- ${CURRENTVERSION} - a link to the page history or the move log, with the -- display message "current-version-edit-display" or -- "current-version-move-display". -- -- ${EDITREQUEST} - a link to create an edit request for the current page. -- -- ${EXPLANATIONBLURB} - an explanation blurb, e.g. "Please discuss any changes -- on the talk page; you may submit a request to ask an administrator to make -- an edit if it is minor or supported by consensus." -- -- ${IMAGELINK} - a link to set the image to, depending on the protection -- action and protection level. -- -- ${INTROBLURB} - the PROTECTIONBLURB parameter, plus the expiry if an expiry -- is set. E.g. "Editing of this page by new or unregistered users is currently -- disabled until dd Month YYYY." -- -- ${INTROFRAGMENT} - the same as ${INTROBLURB}, but without final punctuation -- so that it can be used in run-on sentences. -- -- ${PAGETYPE} - the type of the page, e.g. "article" or "template". -- Defined in the cfg.pagetypes table. -- -- ${PROTECTIONBLURB} - a blurb explaining the protection level of the page, e.g. -- "Editing of this page by new or unregistered users is currently disabled" -- -- ${PROTECTIONDATE} - the protection date, if it has been supplied to the -- template. -- -- ${PROTECTIONLEVEL} - the protection level, e.g. "fully protected" or -- "semi-protected". -- -- ${PROTECTIONLOG} - a link to the protection log or the pending changes log, -- depending on the protection action. -- -- ${TALKPAGE} - a link to the talk page. If a section is specified, links -- straight to that talk page section. -- -- ${TOOLTIPBLURB} - uses the PAGETYPE, PROTECTIONTYPE and EXPIRY parameters to -- create a blurb like "This template is semi-protected", or "This article is -- move-protected until DD Month YYYY". -- -- ${VANDAL} - links for the specified username (or the root page name) -- using Module:Vandal-m. -- -- Functions -- -- For advanced users, it is possible to use Lua functions instead of strings -- in the banner config tables. Using functions gives flexibility that is not -- possible just by using parameters. Functions take two arguments, the -- protection object and the template arguments, and they must output a string. -- -- For example: -- -- text = function (protectionObj, args) -- if protectionObj.level == 'autoconfirmed' then -- return 'foo' -- else -- return 'bar' -- end -- end -- -- Some protection object properties and methods that may be useful: -- protectionObj.action - the protection action -- protectionObj.level - the protection level -- protectionObj.reason - the protection reason -- protectionObj.expiry - the expiry. Nil if unset, the string "indef" if set -- to indefinite, and the protection time in unix time if temporary. -- protectionObj.protectionDate - the protection date in unix time, or nil if -- unspecified. -- protectionObj.bannerConfig - the banner config found by the module. Beware -- of editing the config field used by the function, as it could create an -- infinite loop. -- protectionObj:isProtected - returns a boolean showing whether the page is -- protected. -- protectionObj:isTemporary - returns a boolean showing whether the expiry is -- temporary. -- protectionObj:isIncorrect - returns a boolean showing whether the protection -- template is incorrect. --]] -- The master banner data, used if no values have been found in banners or -- defaultBanners. masterBanner = { text = '${INTROBLURB}', explanation = '${EXPLANATIONBLURB}', tooltip = '${TOOLTIPBLURB}', link = '${IMAGELINK}', alt = 'Page ${PROTECTIONLEVEL}' }, -- The default banner data. This holds banner data for different protection -- levels. -- *required* - this table needs edit, move, autoreview and upload subtables. defaultBanners = { edit = {}, move = {}, autoreview = { default = { alt = 'Page protected with pending changes', tooltip = 'All edits by unregistered and new users are subject to review prior to becoming visible to unregistered users', image = 'Pending-protection-shackle.svg' } }, upload = {} }, -- The banner data. This holds banner data for different protection reasons. -- In fact, the reasons specified in this table control which reasons are -- valid inputs to the first positional parameter. -- -- There is also a non-standard "description" field that can be used for items -- in this table. This is a description of the protection reason for use in the -- module documentation. -- -- *required* - this table needs edit, move, autoreview and upload subtables. banners = { edit = { blp = { description = 'For pages protected to promote compliance with the' .. ' [[Wikipedia:Biographies of living persons' .. '|biographies of living persons]] policy', text = '${INTROFRAGMENT} to promote compliance with' .. ' [[Wikipedia:Biographies of living persons' .. "|Wikipedia's&nbsp;policy on&nbsp;the&nbsp;biographies" .. ' of&nbsp;living&nbsp;people]].', tooltip = '${TOOLTIPFRAGMENT} to promote compliance with the policy on' .. ' biographies of living persons', }, dmca = { description = 'For pages protected by the Wikimedia Foundation' .. ' due to [[Digital Millennium Copyright Act]] takedown requests', explanation = function (protectionObj, args) local ret = 'Pursuant to a rights owner notice under the Digital' .. ' Millennium Copyright Act (DMCA) regarding some content' .. ' in this article, the Wikimedia Foundation acted under' .. ' applicable law and took down and restricted the content' .. ' in question.' if args.notice then ret = ret .. ' A copy of the received notice can be found here: ' .. args.notice .. '.' end ret = ret .. ' For more information, including websites discussing' .. ' how to file a counter-notice, please see' .. " [[Wikipedia:Office actions]] and the article's ${TALKPAGE}." .. "'''Do not remove this template from the article until the" .. " restrictions are withdrawn'''." return ret end, image = 'Office-protection-shackle.svg', }, dispute = { description = 'For pages protected due to editing disputes', text = function (protectionObj, args) -- Find the value of "disputes". local display = 'disputes' local disputes if args.section then disputes = string.format( '[[%s:%s#%s|%s]]', mw.site.namespaces[protectionObj.title.namespace].talk.name, protectionObj.title.text, args.section, display ) else disputes = display end -- Make the blurb, depending on the expiry. local msg if type(protectionObj.expiry) == 'number' then msg = '${INTROFRAGMENT} or until editing %s have been resolved.' else msg = '${INTROFRAGMENT} until editing %s have been resolved.' end return string.format(msg, disputes) end, explanation = "This protection is '''not''' an endorsement of the" .. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}', tooltip = '${TOOLTIPFRAGMENT} due to editing disputes', }, ecp = { description = 'For articles in topic areas authorized by' .. ' [[Wikipedia:Arbitration Committee|ArbCom]] or' .. ' meets the criteria for community use', tooltip = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}', alt = 'Extended-protected ${PAGETYPE}', }, mainpage = { description = 'For pages protected for being displayed on the [[Main Page]]', text = 'This file is currently' .. ' [[Wikipedia:This page is protected|protected]] from' .. ' editing because it is currently or will soon be displayed' .. ' on the [[Main Page]].', explanation = 'Images on the Main Page are protected due to their high' .. ' visibility. Please discuss any necessary changes on the ${TALKPAGE}.' .. '<br /><span style="font-size:90%;">' .. "'''Administrators:''' Once this image is definitely off the Main Page," .. ' please unprotect this file, or reduce to semi-protection,' .. ' as appropriate.</span>', }, office = { description = 'For pages protected by the Wikimedia Foundation', text = function (protectionObj, args) local ret = 'This ${PAGETYPE} is currently under the' .. ' scrutiny of the' .. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]' .. ' and is protected.' if protectionObj.protectionDate then ret = ret .. ' It has been protected since ${PROTECTIONDATE}.' end return ret end, explanation = "If you can edit this page, please discuss all changes and" .. " additions on the ${TALKPAGE} first. '''Do not remove protection from this" .. " page unless you are authorized by the Wikimedia Foundation to do" .. " so.'''", image = 'Office-protection-shackle.svg', }, reset = { description = 'For pages protected by the Wikimedia Foundation and' .. ' "reset" to a bare-bones version', text = 'This ${PAGETYPE} is currently under the' .. ' scrutiny of the' .. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]' .. ' and is protected.', explanation = function (protectionObj, args) local ret = '' if protectionObj.protectionDate then ret = ret .. 'On ${PROTECTIONDATE} this ${PAGETYPE} was' else ret = ret .. 'This ${PAGETYPE} has been' end ret = ret .. ' reduced to a' .. ' simplified, "bare bones" version so that it may be completely' .. ' rewritten to ensure it meets the policies of' .. ' [[WP:NPOV|Neutral Point of View]] and [[WP:V|Verifiability]].' .. ' Standard Wikipedia policies will apply to its rewriting—which' .. ' will eventually be open to all editors—and will be strictly' .. ' enforced. The ${PAGETYPE} has been ${PROTECTIONLEVEL} while' .. ' it is being rebuilt.\n\n' .. 'Any insertion of material directly from' .. ' pre-protection revisions of the ${PAGETYPE} will be removed, as' .. ' will any material added to the ${PAGETYPE} that is not properly' .. ' sourced. The associated talk page(s) were also cleared on the' .. " same date.\n\n" .. "If you can edit this page, please discuss all changes and" .. " additions on the ${TALKPAGE} first. '''Do not override" .. " this action, and do not remove protection from this page," .. " unless you are authorized by the Wikimedia Foundation" .. " to do so. No editor may remove this notice.'''" return ret end, image = 'Office-protection-shackle.svg', }, sock = { description = 'For pages protected due to' .. ' [[Wikipedia:Sock puppetry|sock puppetry]]', text = '${INTROFRAGMENT} to prevent [[Wikipedia:Sock puppetry|sock puppets]] of' .. ' [[Wikipedia:Blocking policy|blocked]] or' .. ' [[Wikipedia:Banning policy|banned users]]' .. ' from editing it.', tooltip = '${TOOLTIPFRAGMENT} to prevent sock puppets of blocked or banned users from' .. ' editing it', }, template = { description = 'For [[Wikipedia:High-risk templates|high-risk]]' .. ' templates and Lua modules', text = 'This is a permanently [[Help:Protection|protected]] ${PAGETYPE},' .. ' as it is [[Wikipedia:High-risk templates|high-risk]].', explanation = 'Please discuss any changes on the ${TALKPAGE}; you may' .. ' ${EDITREQUEST} to ask an' .. ' [[Wikipedia:Administrators|administrator]] or' .. ' [[Wikipedia:Template editor|template editor]] to make an edit if' .. ' it is [[Help:Minor edit#When to mark an edit as a minor edit' .. '|uncontroversial]] or supported by' .. ' [[Wikipedia:Consensus|consensus]]. You can also' .. ' [[Wikipedia:Requests for page protection|request]] that the page be' .. ' unprotected.', tooltip = 'This high-risk ${PAGETYPE} is permanently ${PROTECTIONLEVEL}' .. ' to prevent vandalism', alt = 'Permanently protected ${PAGETYPE}', }, usertalk = { description = 'For pages protected against disruptive edits by a' .. ' particular user', text = '${INTROFRAGMENT} to prevent ${VANDAL} from using it to make disruptive edits,' .. ' such as abusing the' .. ' &#123;&#123;[[Template:unblock|unblock]]&#125;&#125; template.', explanation = 'If you cannot edit this user talk page and you need to' .. ' make a change or leave a message, you can' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for edits to a protected page' .. '|request an edit]],' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]],' .. ' [[Special:Userlogin|log in]],' .. ' or [[Special:UserLogin/signup|create an account]].', }, vandalism = { description = 'For pages protected against' .. ' [[Wikipedia:Vandalism|vandalism]]', text = '${INTROFRAGMENT} due to [[Wikipedia:Vandalism|vandalism]].', explanation = function (protectionObj, args) local ret = '' if protectionObj.level == 'sysop' then ret = ret .. "This protection is '''not''' an endorsement of the" .. ' ${CURRENTVERSION}. ' end return ret .. '${EXPLANATIONBLURB}' end, tooltip = '${TOOLTIPFRAGMENT} due to vandalism', } }, move = { dispute = { description = 'For pages protected against page moves due to' .. ' disputes over the page title', explanation = "This protection is '''not''' an endorsement of the" .. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}', image = 'Move-protection-shackle.svg' }, vandalism = { description = 'For pages protected against' .. ' [[Wikipedia:Vandalism#Page-move vandalism' .. ' |page-move vandalism]]' } }, autoreview = {}, upload = {} }, -------------------------------------------------------------------------------- -- -- GENERAL DATA TABLES -- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- Protection blurbs -------------------------------------------------------------------------------- -- This table produces the protection blurbs available with the -- ${PROTECTIONBLURB} parameter. It is sorted by protection action and -- protection level, and is checked by the module in the following order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level -- 3. "edit" protection action, default protection level -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. protectionBlurbs = { edit = { default = 'This ${PAGETYPE} is currently [[Help:Protection|' .. 'protected]] from editing', autoconfirmed = 'Editing of this ${PAGETYPE} by [[Wikipedia:User access' .. ' levels#New users|new]] or [[Wikipedia:User access levels#Unregistered' .. ' users|unregistered]] users is currently [[Help:Protection|disabled]]', extendedconfirmed = 'This ${PAGETYPE} is currently under extended confirmed protection', }, move = { default = 'This ${PAGETYPE} is currently [[Help:Protection|protected]]' .. ' from [[Help:Moving a page|page moves]]' }, autoreview = { default = 'All edits made to this ${PAGETYPE} by' .. ' [[Wikipedia:User access levels#New users|new]] or' .. ' [[Wikipedia:User access levels#Unregistered users|unregistered]]' .. ' users are currently' .. ' [[Wikipedia:Pending changes|subject to review]]' }, upload = { default = 'Uploading new versions of this ${PAGETYPE} is currently disabled' } }, -------------------------------------------------------------------------------- -- Explanation blurbs -------------------------------------------------------------------------------- -- This table produces the explanation blurbs available with the -- ${EXPLANATIONBLURB} parameter. It is sorted by protection action, -- protection level, and whether the page is a talk page or not. If the page is -- a talk page it will have a talk key of "talk"; otherwise it will have a talk -- key of "subject". The table is checked in the following order: -- 1. page's protection action, page's protection level, page's talk key -- 2. page's protection action, page's protection level, default talk key -- 3. page's protection action, default protection level, page's talk key -- 4. page's protection action, default protection level, default talk key -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. explanationBlurbs = { edit = { autoconfirmed = { subject = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details. If you' .. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can' .. ' ${EDITREQUEST}, discuss changes on the ${TALKPAGE},' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]], [[Special:Userlogin|log in]], or' .. ' [[Special:UserLogin/signup|create an account]].', default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details. If you' .. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]], [[Special:Userlogin|log in]], or' .. ' [[Special:UserLogin/signup|create an account]].', }, extendedconfirmed = { default = 'Extended confirmed protection prevents edits from all unregistered editors' .. ' and registered users with fewer than 30 days tenure and 500 edits.' .. ' The [[Wikipedia:Protection policy#extended|policy on community use]]' .. ' specifies that extended confirmed protection can be applied to combat' .. ' disruption, if semi-protection has proven to be ineffective.' .. ' Extended confirmed protection may also be applied to enforce' .. ' [[Wikipedia:Arbitration Committee|arbitration sanctions]].' .. ' Please discuss any changes on the ${TALKPAGE}; you may' .. ' ${EDITREQUEST} to ask for uncontroversial changes supported by' .. ' [[Wikipedia:Consensus|consensus]].' }, default = { subject = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' Please discuss any changes on the ${TALKPAGE}; you' .. ' may ${EDITREQUEST} to ask an' .. ' [[Wikipedia:Administrators|administrator]] to make an edit if it' .. ' is [[Help:Minor edit#When to mark an edit as a minor edit' .. '|uncontroversial]] or supported by [[Wikipedia:Consensus' .. '|consensus]]. You may also [[Wikipedia:Requests for' .. ' page protection#Current requests for reduction in protection level' .. '|request]] that this page be unprotected.', default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' You may [[Wikipedia:Requests for page' .. ' protection#Current requests for edits to a protected page|request an' .. ' edit]] to this page, or [[Wikipedia:Requests for' .. ' page protection#Current requests for reduction in protection level' .. '|ask]] for it to be unprotected.' } }, move = { default = { subject = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' The page may still be edited but cannot be moved' .. ' until unprotected. Please discuss any suggested moves on the' .. ' ${TALKPAGE} or at [[Wikipedia:Requested moves]]. You can also' .. ' [[Wikipedia:Requests for page protection|request]] that the page be' .. ' unprotected.', default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' The page may still be edited but cannot be moved' .. ' until unprotected. Please discuss any suggested moves at' .. ' [[Wikipedia:Requested moves]]. You can also' .. ' [[Wikipedia:Requests for page protection|request]] that the page be' .. ' unprotected.' } }, autoreview = { default = { default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' Edits to this ${PAGETYPE} by new and unregistered users' .. ' will not be visible to readers until they are accepted by' .. ' a reviewer. To avoid the need for your edits to be' .. ' reviewed, you may' .. ' [[Wikipedia:Requests for page protection' .. '#Current requests for reduction in protection level' .. '|request unprotection]], [[Special:Userlogin|log in]], or' .. ' [[Special:UserLogin/signup|create an account]].' }, }, upload = { default = { default = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' The page may still be edited but new versions of the file' .. ' cannot be uploaded until it is unprotected. You can' .. ' request that a new version be uploaded by using a' .. ' [[Wikipedia:Edit requests|protected edit request]], or you' .. ' can [[Wikipedia:Requests for page protection|request]]' .. ' that the file be unprotected.' } } }, -------------------------------------------------------------------------------- -- Protection levels -------------------------------------------------------------------------------- -- This table provides the data for the ${PROTECTIONLEVEL} parameter, which -- produces a short label for different protection levels. It is sorted by -- protection action and protection level, and is checked in the following -- order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level -- 3. "edit" protection action, default protection level -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. protectionLevels = { edit = { default = 'protected', templateeditor = 'template-protected', extendedconfirmed = 'extended-protected', autoconfirmed = 'semi-protected', }, move = { default = 'move-protected' }, autoreview = { }, upload = { default = 'upload-protected' } }, -------------------------------------------------------------------------------- -- Images -------------------------------------------------------------------------------- -- This table lists different padlock images for each protection action and -- protection level. It is used if an image is not specified in any of the -- banner data tables, and if the page does not satisfy the conditions for using -- the ['image-filename-indef'] image. It is checked in the following order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level images = { edit = { default = 'Full-protection-shackle.svg', templateeditor = 'Template-protection-shackle.svg', extendedconfirmed = 'Extended-protection-shackle.svg', autoconfirmed = 'Semi-protection-shackle.svg' }, move = { default = 'Move-protection-shackle.svg', }, autoreview = { default = 'Pending-protection-shackle.svg' }, upload = { default = 'Upload-protection-shackle.svg' } }, -- Pages with a reason specified in this table will show the special "indef" -- padlock, defined in the 'image-filename-indef' message, if no expiry is set. indefImageReasons = { template = true }, -------------------------------------------------------------------------------- -- Image links -------------------------------------------------------------------------------- -- This table provides the data for the ${IMAGELINK} parameter, which gets -- the image link for small padlock icons based on the page's protection action -- and protection level. It is checked in the following order: -- 1. page's protection action, page's protection level -- 2. page's protection action, default protection level -- 3. "edit" protection action, default protection level -- -- It is possible to use banner parameters inside this table. -- *required* - this table needs edit, move, autoreview and upload subtables. imageLinks = { edit = { default = 'Wikipedia:Protection policy#full', templateeditor = 'Wikipedia:Protection policy#template', extendedconfirmed = 'Wikipedia:Protection policy#extended', autoconfirmed = 'Wikipedia:Protection policy#semi' }, move = { default = 'Wikipedia:Protection policy#move' }, autoreview = { default = 'Wikipedia:Protection policy#pending' }, upload = { default = 'Wikipedia:Protection policy#upload' } }, -------------------------------------------------------------------------------- -- Padlock indicator names -------------------------------------------------------------------------------- -- This table provides the "name" attribute for the <indicator> extension tag -- with which small padlock icons are generated. All indicator tags on a page -- are displayed in alphabetical order based on this attribute, and with -- indicator tags with duplicate names, the last tag on the page wins. -- The attribute is chosen based on the protection action; table keys must be a -- protection action name or the string "default". padlockIndicatorNames = { autoreview = 'pp-autoreview', default = 'pp-default' }, -------------------------------------------------------------------------------- -- Protection categories -------------------------------------------------------------------------------- --[[ -- The protection categories are stored in the protectionCategories table. -- Keys to this table are made up of the following strings: -- -- 1. the expiry date -- 2. the namespace -- 3. the protection reason (e.g. "dispute" or "vandalism") -- 4. the protection level (e.g. "sysop" or "autoconfirmed") -- 5. the action (e.g. "edit" or "move") -- -- When the module looks up a category in the table, first it will will check to -- see a key exists that corresponds to all five parameters. For example, a -- user page semi-protected from vandalism for two weeks would have the key -- "temp-user-vandalism-autoconfirmed-edit". If no match is found, the module -- changes the first part of the key to "all" and checks the table again. It -- keeps checking increasingly generic key combinations until it finds the -- field, or until it reaches the key "all-all-all-all-all". -- -- The module uses a binary matrix to determine the order in which to search. -- This is best demonstrated by a table. In this table, the "0" values -- represent "all", and the "1" values represent the original data (e.g. -- "indef" or "file" or "vandalism"). -- -- expiry namespace reason level action -- order -- 1 1 1 1 1 1 -- 2 0 1 1 1 1 -- 3 1 0 1 1 1 -- 4 0 0 1 1 1 -- 5 1 1 0 1 1 -- 6 0 1 0 1 1 -- 7 1 0 0 1 1 -- 8 0 0 0 1 1 -- 9 1 1 1 0 1 -- 10 0 1 1 0 1 -- 11 1 0 1 0 1 -- 12 0 0 1 0 1 -- 13 1 1 0 0 1 -- 14 0 1 0 0 1 -- 15 1 0 0 0 1 -- 16 0 0 0 0 1 -- 17 1 1 1 1 0 -- 18 0 1 1 1 0 -- 19 1 0 1 1 0 -- 20 0 0 1 1 0 -- 21 1 1 0 1 0 -- 22 0 1 0 1 0 -- 23 1 0 0 1 0 -- 24 0 0 0 1 0 -- 25 1 1 1 0 0 -- 26 0 1 1 0 0 -- 27 1 0 1 0 0 -- 28 0 0 1 0 0 -- 29 1 1 0 0 0 -- 30 0 1 0 0 0 -- 31 1 0 0 0 0 -- 32 0 0 0 0 0 -- -- In this scheme the action has the highest priority, as it is the last -- to change, and the expiry has the least priority, as it changes the most. -- The priorities of the expiry, the protection level and the action are -- fixed, but the priorities of the reason and the namespace can be swapped -- through the use of the cfg.bannerDataNamespaceHasPriority table. --]] -- If the reason specified to the template is listed in this table, -- namespace data will take priority over reason data in the protectionCategories -- table. reasonsWithNamespacePriority = { vandalism = true, }, -- The string to use as a namespace key for the protectionCategories table for each -- namespace number. categoryNamespaceKeys = { [ 2] = 'user', [ 3] = 'user', [ 4] = 'project', [ 6] = 'file', [ 8] = 'mediawiki', [ 10] = 'template', [ 12] = 'project', [ 14] = 'category', [100] = 'portal', [828] = 'module', }, protectionCategories = { ['all|all|all|all|all'] = 'Wikipedia fully protected pages', ['all|all|office|all|all'] = 'Wikipedia Office-protected pages', ['all|all|reset|all|all'] = 'Wikipedia Office-protected pages', ['all|all|dmca|all|all'] = 'Wikipedia Office-protected pages', ['all|all|mainpage|all|all'] = 'Wikipedia fully protected main page files', ['all|all|all|extendedconfirmed|all'] = 'Wikipedia extended-confirmed-protected pages', ['all|all|ecp|extendedconfirmed|all'] = 'Wikipedia extended-confirmed-protected pages', ['all|template|all|all|edit'] = 'Wikipedia fully protected templates', ['all|all|all|autoconfirmed|edit'] = 'Wikipedia semi-protected pages', ['indef|all|all|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected pages', ['all|all|blp|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected biographies of living people', ['temp|all|blp|autoconfirmed|edit'] = 'Wikipedia temporarily semi-protected biographies of living people', ['all|all|dispute|autoconfirmed|edit'] = 'Wikipedia pages semi-protected due to dispute', ['all|all|sock|autoconfirmed|edit'] = 'Wikipedia pages semi-protected from banned users', ['all|all|vandalism|autoconfirmed|edit'] = 'Wikipedia pages semi-protected against vandalism', ['all|category|all|autoconfirmed|edit'] = 'Wikipedia semi-protected categories', ['all|file|all|autoconfirmed|edit'] = 'Wikipedia semi-protected files', ['all|portal|all|autoconfirmed|edit'] = 'Wikipedia semi-protected portals', ['all|project|all|autoconfirmed|edit'] = 'Wikipedia semi-protected project pages', ['all|talk|all|autoconfirmed|edit'] = 'Wikipedia semi-protected talk pages', ['all|template|all|autoconfirmed|edit'] = 'Wikipedia semi-protected templates', ['all|user|all|autoconfirmed|edit'] = 'Wikipedia semi-protected user and user talk pages', ['all|all|all|templateeditor|edit'] = 'Wikipedia template-protected pages other than templates and modules', ['all|template|all|templateeditor|edit'] = 'Wikipedia template-protected templates', ['all|template|all|templateeditor|move'] = 'Wikipedia template-protected templates', -- move-protected templates ['all|all|blp|sysop|edit'] = 'Wikipedia indefinitely protected biographies of living people', ['temp|all|blp|sysop|edit'] = 'Wikipedia temporarily protected biographies of living people', ['all|all|dispute|sysop|edit'] = 'Wikipedia pages protected due to dispute', ['all|all|sock|sysop|edit'] = 'Wikipedia pages protected from banned users', ['all|all|vandalism|sysop|edit'] = 'Wikipedia pages protected against vandalism', ['all|category|all|sysop|edit'] = 'Wikipedia fully protected categories', ['all|file|all|sysop|edit'] = 'Wikipedia fully protected files', ['all|project|all|sysop|edit'] = 'Wikipedia fully protected project pages', ['all|talk|all|sysop|edit'] = 'Wikipedia fully protected talk pages', ['all|template|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected templates', ['all|template|all|sysop|edit'] = 'Wikipedia fully protected templates', ['all|user|all|sysop|edit'] = 'Wikipedia fully protected user and user talk pages', ['all|module|all|all|edit'] = 'Wikipedia fully protected modules', ['all|module|all|templateeditor|edit'] = 'Wikipedia template-protected modules', ['all|module|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected modules', ['all|module|all|autoconfirmed|edit'] = 'Wikipedia semi-protected modules', ['all|all|all|sysop|move'] = 'Wikipedia move-protected pages', ['indef|all|all|sysop|move'] = 'Wikipedia indefinitely move-protected pages', ['all|all|dispute|sysop|move'] = 'Wikipedia pages move-protected due to dispute', ['all|all|vandalism|sysop|move'] = 'Wikipedia pages move-protected due to vandalism', ['all|portal|all|sysop|move'] = 'Wikipedia move-protected portals', ['all|project|all|sysop|move'] = 'Wikipedia move-protected project pages', ['all|talk|all|sysop|move'] = 'Wikipedia move-protected talk pages', ['all|template|all|sysop|move'] = 'Wikipedia move-protected templates', ['all|user|all|sysop|move'] = 'Wikipedia move-protected user and user talk pages', ['all|all|all|autoconfirmed|autoreview'] = 'Wikipedia pending changes protected pages', ['all|file|all|all|upload'] = 'Wikipedia upload-protected files', }, -------------------------------------------------------------------------------- -- Expiry category config -------------------------------------------------------------------------------- -- This table configures the expiry category behaviour for each protection -- action. -- * If set to true, setting that action will always categorise the page if -- an expiry parameter is not set. -- * If set to false, setting that action will never categorise the page. -- * If set to nil, the module will categorise the page if: -- 1) an expiry parameter is not set, and -- 2) a reason is provided, and -- 3) the specified reason is not blacklisted in the reasonsWithoutExpiryCheck -- table. expiryCheckActions = { edit = nil, move = false, autoreview = true, upload = false }, reasonsWithoutExpiryCheck = { blp = true, template = true, }, -------------------------------------------------------------------------------- -- Pagetypes -------------------------------------------------------------------------------- -- This table produces the page types available with the ${PAGETYPE} parameter. -- Keys are namespace numbers, or the string "default" for the default value. pagetypes = { [0] = 'article', [6] = 'file', [10] = 'template', [14] = 'category', [828] = 'module', default = 'page' }, -------------------------------------------------------------------------------- -- Strings marking indefinite protection -------------------------------------------------------------------------------- -- This table contains values passed to the expiry parameter that mean the page -- is protected indefinitely. indefStrings = { ['indef'] = true, ['indefinite'] = true, ['indefinitely'] = true, ['infinite'] = true, }, -------------------------------------------------------------------------------- -- Group hierarchy -------------------------------------------------------------------------------- -- This table maps each group to all groups that have a superset of the original -- group's page editing permissions. hierarchy = { sysop = {}, reviewer = {'sysop'}, filemover = {'sysop'}, templateeditor = {'sysop'}, extendedconfirmed = {'sysop'}, autoconfirmed = {'reviewer', 'filemover', 'templateeditor', 'extendedconfirmed'}, user = {'autoconfirmed'}, ['*'] = {'user'} }, -------------------------------------------------------------------------------- -- Wrapper templates and their default arguments -------------------------------------------------------------------------------- -- This table contains wrapper templates used with the module, and their -- default arguments. Templates specified in this table should contain the -- following invocation, and no other template content: -- -- {{#invoke:Protection banner|main}} -- -- If other content is desired, it can be added between -- <noinclude>...</noinclude> tags. -- -- When a user calls one of these wrapper templates, they will use the -- default arguments automatically. However, users can override any of the -- arguments. wrappers = { ['Template:Pp'] = {}, ['Template:Pp-extended'] = {'ecp'}, ['Template:Pp-blp'] = {'blp'}, -- we don't need Template:Pp-create ['Template:Pp-dispute'] = {'dispute'}, ['Template:Pp-main-page'] = {'mainpage'}, ['Template:Pp-move'] = {action = 'move', catonly = 'yes'}, ['Template:Pp-move-dispute'] = {'dispute', action = 'move', catonly = 'yes'}, -- we don't need Template:Pp-move-indef ['Template:Pp-move-vandalism'] = {'vandalism', action = 'move', catonly = 'yes'}, ['Template:Pp-office'] = {'office'}, ['Template:Pp-office-dmca'] = {'dmca'}, ['Template:Pp-pc'] = {action = 'autoreview', small = true}, ['Template:Pp-pc1'] = {action = 'autoreview', small = true}, ['Template:Pp-reset'] = {'reset'}, ['Template:Pp-semi-indef'] = {small = true}, ['Template:Pp-sock'] = {'sock'}, ['Template:Pp-template'] = {'template', small = true}, ['Template:Pp-upload'] = {action = 'upload'}, ['Template:Pp-usertalk'] = {'usertalk'}, ['Template:Pp-vandalism'] = {'vandalism'}, }, -------------------------------------------------------------------------------- -- -- MESSAGES -- -------------------------------------------------------------------------------- msg = { -------------------------------------------------------------------------------- -- Intro blurb and intro fragment -------------------------------------------------------------------------------- -- These messages specify what is produced by the ${INTROBLURB} and -- ${INTROFRAGMENT} parameters. If the protection is temporary they use the -- intro-blurb-expiry or intro-fragment-expiry, and if not they use -- intro-blurb-noexpiry or intro-fragment-noexpiry. -- It is possible to use banner parameters in these messages. ['intro-blurb-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY}.', ['intro-blurb-noexpiry'] = '${PROTECTIONBLURB}.', ['intro-fragment-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY},', ['intro-fragment-noexpiry'] = '${PROTECTIONBLURB}', -------------------------------------------------------------------------------- -- Tooltip blurb -------------------------------------------------------------------------------- -- These messages specify what is produced by the ${TOOLTIPBLURB} parameter. -- If the protection is temporary the tooltip-blurb-expiry message is used, and -- if not the tooltip-blurb-noexpiry message is used. -- It is possible to use banner parameters in these messages. ['tooltip-blurb-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY}.', ['tooltip-blurb-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}.', ['tooltip-fragment-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY},', ['tooltip-fragment-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}', -------------------------------------------------------------------------------- -- Special explanation blurb -------------------------------------------------------------------------------- -- An explanation blurb for pages that cannot be unprotected, e.g. for pages -- in the MediaWiki namespace. -- It is possible to use banner parameters in this message. ['explanation-blurb-nounprotect'] = 'See the [[Wikipedia:Protection policy|' .. 'protection policy]] and ${PROTECTIONLOG} for more details.' .. ' Please discuss any changes on the ${TALKPAGE}; you' .. ' may ${EDITREQUEST} to ask an' .. ' [[Wikipedia:Administrators|administrator]] to make an edit if it' .. ' is [[Help:Minor edit#When to mark an edit as a minor edit' .. '|uncontroversial]] or supported by [[Wikipedia:Consensus' .. '|consensus]].', -------------------------------------------------------------------------------- -- Protection log display values -------------------------------------------------------------------------------- -- These messages determine the display values for the protection log link -- or the pending changes log link produced by the ${PROTECTIONLOG} parameter. -- It is possible to use banner parameters in these messages. ['protection-log-display'] = 'protection log', ['pc-log-display'] = 'pending changes log', -------------------------------------------------------------------------------- -- Current version display values -------------------------------------------------------------------------------- -- These messages determine the display values for the page history link -- or the move log link produced by the ${CURRENTVERSION} parameter. -- It is possible to use banner parameters in these messages. ['current-version-move-display'] = 'current title', ['current-version-edit-display'] = 'current version', -------------------------------------------------------------------------------- -- Talk page -------------------------------------------------------------------------------- -- This message determines the display value of the talk page link produced -- with the ${TALKPAGE} parameter. -- It is possible to use banner parameters in this message. ['talk-page-link-display'] = 'talk page', -------------------------------------------------------------------------------- -- Edit requests -------------------------------------------------------------------------------- -- This message determines the display value of the edit request link produced -- with the ${EDITREQUEST} parameter. -- It is possible to use banner parameters in this message. ['edit-request-display'] = 'submit an edit request', -------------------------------------------------------------------------------- -- Expiry date format -------------------------------------------------------------------------------- -- This is the format for the blurb expiry date. It should be valid input for -- the first parameter of the #time parser function. ['expiry-date-format'] = 'F j, Y "at" H:i e', -------------------------------------------------------------------------------- -- Tracking categories -------------------------------------------------------------------------------- -- These messages determine which tracking categories the module outputs. ['tracking-category-incorrect'] = 'Wikipedia pages with incorrect protection templates', ['tracking-category-template'] = 'Wikipedia template-protected pages other than templates and modules', -------------------------------------------------------------------------------- -- Images -------------------------------------------------------------------------------- -- These are images that are not defined by their protection action and protection level. ['image-filename-indef'] = 'Full-protection-shackle.svg', ['image-filename-default'] = 'Transparent.gif', -------------------------------------------------------------------------------- -- End messages -------------------------------------------------------------------------------- } -------------------------------------------------------------------------------- -- End configuration -------------------------------------------------------------------------------- } a20552ae38cb5253a4fa29aa126abc74215a589f Module:Ombox 828 359 713 712 2023-07-10T15:44:42Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Ombox]] wikitext text/x-wiki {{#invoke:Message box|ombox}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 0e54065432d540737b9e56c4e3a8e7f74d4534ea Module:TNT 828 360 715 714 2023-07-10T15:44:42Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:TNT]] Scribunto text/plain -- -- INTRO: (!!! DO NOT RENAME THIS PAGE !!!) -- This module allows any template or module to be copy/pasted between -- wikis without any translation changes. All translation text is stored -- in the global Data:*.tab pages on Commons, and used everywhere. -- -- SEE: https://www.mediawiki.org/wiki/Multilingual_Templates_and_Modules -- -- ATTENTION: -- Please do NOT rename this module - it has to be identical on all wikis. -- This code is maintained at https://www.mediawiki.org/wiki/Module:TNT -- Please do not modify it anywhere else, as it may get copied and override your changes. -- Suggestions can be made at https://www.mediawiki.org/wiki/Module_talk:TNT -- -- DESCRIPTION: -- The "msg" function uses a Commons dataset to translate a message -- with a given key (e.g. source-table), plus optional arguments -- to the wiki markup in the current content language. -- Use lang=xx to set language. Example: -- -- {{#invoke:TNT | msg -- | I18n/Template:Graphs.tab <!-- https://commons.wikimedia.org/wiki/Data:I18n/Template:Graphs.tab --> -- | source-table <!-- uses a translation message with id = "source-table" --> -- | param1 }} <!-- optional parameter --> -- -- -- The "doc" function will generate the <templatedata> parameter documentation for templates. -- This way all template parameters can be stored and localized in a single Commons dataset. -- NOTE: "doc" assumes that all documentation is located in Data:Templatedata/* on Commons. -- -- {{#invoke:TNT | doc | Graph:Lines }} -- uses https://commons.wikimedia.org/wiki/Data:Templatedata/Graph:Lines.tab -- if the current page is Template:Graph:Lines/doc -- local p = {} local i18nDataset = 'I18n/Module:TNT.tab' -- Forward declaration of the local functions local sanitizeDataset, loadData, link, formatMessage function p.msg(frame) local dataset, id local params = {} local lang = nil for k, v in pairs(frame.args) do if k == 1 then dataset = mw.text.trim(v) elseif k == 2 then id = mw.text.trim(v) elseif type(k) == 'number' then table.insert(params, mw.text.trim(v)) elseif k == 'lang' and v ~= '_' then lang = mw.text.trim(v) end end return formatMessage(dataset, id, params, lang) end -- Identical to p.msg() above, but used from other lua modules -- Parameters: name of dataset, message key, optional arguments -- Example with 2 params: format('I18n/Module:TNT', 'error_bad_msgkey', 'my-key', 'my-dataset') function p.format(dataset, key, ...) local checkType = require('libraryUtil').checkType checkType('format', 1, dataset, 'string') checkType('format', 2, key, 'string') return formatMessage(dataset, key, {...}) end -- Identical to p.msg() above, but used from other lua modules with the language param -- Parameters: language code, name of dataset, message key, optional arguments -- Example with 2 params: formatInLanguage('es', I18n/Module:TNT', 'error_bad_msgkey', 'my-key', 'my-dataset') function p.formatInLanguage(lang, dataset, key, ...) local checkType = require('libraryUtil').checkType checkType('formatInLanguage', 1, lang, 'string') checkType('formatInLanguage', 2, dataset, 'string') checkType('formatInLanguage', 3, key, 'string') return formatMessage(dataset, key, {...}, lang) end -- Obsolete function that adds a 'c:' prefix to the first param. -- "Sandbox/Sample.tab" -> 'c:Data:Sandbox/Sample.tab' function p.link(frame) return link(frame.args[1]) end function p.doc(frame) local dataset = 'Templatedata/' .. sanitizeDataset(frame.args[1]) return frame:extensionTag('templatedata', p.getTemplateData(dataset)) .. formatMessage(i18nDataset, 'edit_doc', {link(dataset)}) end function p.getTemplateData(dataset) -- TODO: add '_' parameter once lua starts reindexing properly for "all" languages local data = loadData(dataset) local names = {} for _, field in pairs(data.schema.fields) do table.insert(names, field.name) end local params = {} local paramOrder = {} for _, row in pairs(data.data) do local newVal = {} local name = nil for pos, val in pairs(row) do local columnName = names[pos] if columnName == 'name' then name = val else newVal[columnName] = val end end if name then params[name] = newVal table.insert(paramOrder, name) end end -- Work around json encoding treating {"1":{...}} as an [{...}] params['zzz123']='' local json = mw.text.jsonEncode({ params=params, paramOrder=paramOrder, description=data.description }) json = string.gsub(json,'"zzz123":"",?', "") return json end -- Local functions sanitizeDataset = function(dataset) if not dataset then return nil end dataset = mw.text.trim(dataset) if dataset == '' then return nil elseif string.sub(dataset,-4) ~= '.tab' then return dataset .. '.tab' else return dataset end end loadData = function(dataset, lang) dataset = sanitizeDataset(dataset) if not dataset then error(formatMessage(i18nDataset, 'error_no_dataset', {})) end -- Give helpful error to thirdparties who try and copy this module. if not mw.ext or not mw.ext.data or not mw.ext.data.get then error('Missing JsonConfig extension; Cannot load https://commons.wikimedia.org/wiki/Data:' .. dataset) end local data = mw.ext.data.get(dataset, lang) if data == false then if dataset == i18nDataset then -- Prevent cyclical calls error('Missing Commons dataset ' .. i18nDataset) else error(formatMessage(i18nDataset, 'error_bad_dataset', {link(dataset)})) end end return data end -- Given a dataset name, convert it to a title with the 'commons:data:' prefix link = function(dataset) return 'c:Data:' .. mw.text.trim(dataset or '') end formatMessage = function(dataset, key, params, lang) for _, row in pairs(loadData(dataset, lang).data) do local id, msg = unpack(row) if id == key then local result = mw.message.newRawMessage(msg, unpack(params or {})) return result:plain() end end if dataset == i18nDataset then -- Prevent cyclical calls error('Invalid message key "' .. key .. '"') else error(formatMessage(i18nDataset, 'error_bad_msgkey', {key, link(dataset)})) end end return p 9d0d10e54abd232c806dcabccaf03e52858634a1 Module:Documentation/config 828 361 717 716 2023-07-10T15:44:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Documentation/config]] Scribunto text/plain ---------------------------------------------------------------------------------------------------- -- -- Configuration for Module:Documentation -- -- Here you can set the values of the parameters and messages used in Module:Documentation to -- localise it to your wiki and your language. Unless specified otherwise, values given here -- should be string values. ---------------------------------------------------------------------------------------------------- local cfg = {} -- Do not edit this line. ---------------------------------------------------------------------------------------------------- -- Protection template configuration ---------------------------------------------------------------------------------------------------- -- cfg['protection-reason-edit'] -- The protection reason for edit-protected templates to pass to -- [[Module:Protection banner]]. cfg['protection-reason-edit'] = 'template' --[[ ---------------------------------------------------------------------------------------------------- -- Sandbox notice configuration -- -- On sandbox pages the module can display a template notifying users that the current page is a -- sandbox, and the location of test cases pages, etc. The module decides whether the page is a -- sandbox or not based on the value of cfg['sandbox-subpage']. The following settings configure the -- messages that the notices contains. ---------------------------------------------------------------------------------------------------- --]] -- cfg['sandbox-notice-image'] -- The image displayed in the sandbox notice. cfg['sandbox-notice-image'] = '[[File:Sandbox.svg|50px|alt=|link=]]' --[[ -- cfg['sandbox-notice-pagetype-template'] -- cfg['sandbox-notice-pagetype-module'] -- cfg['sandbox-notice-pagetype-other'] -- The page type of the sandbox page. The message that is displayed depends on the current subject -- namespace. This message is used in either cfg['sandbox-notice-blurb'] or -- cfg['sandbox-notice-diff-blurb']. --]] cfg['sandbox-notice-pagetype-template'] = '[[Wikipedia:Template test cases|template sandbox]] page' cfg['sandbox-notice-pagetype-module'] = '[[Wikipedia:Template test cases|module sandbox]] page' cfg['sandbox-notice-pagetype-other'] = 'sandbox page' --[[ -- cfg['sandbox-notice-blurb'] -- cfg['sandbox-notice-diff-blurb'] -- cfg['sandbox-notice-diff-display'] -- Either cfg['sandbox-notice-blurb'] or cfg['sandbox-notice-diff-blurb'] is the opening sentence -- of the sandbox notice. The latter has a diff link, but the former does not. $1 is the page -- type, which is either cfg['sandbox-notice-pagetype-template'], -- cfg['sandbox-notice-pagetype-module'] or cfg['sandbox-notice-pagetype-other'] depending what -- namespace we are in. $2 is a link to the main template page, and $3 is a diff link between -- the sandbox and the main template. The display value of the diff link is set by -- cfg['sandbox-notice-compare-link-display']. --]] cfg['sandbox-notice-blurb'] = 'This is the $1 for $2.' cfg['sandbox-notice-diff-blurb'] = 'This is the $1 for $2 ($3).' cfg['sandbox-notice-compare-link-display'] = 'diff' --[[ -- cfg['sandbox-notice-testcases-blurb'] -- cfg['sandbox-notice-testcases-link-display'] -- cfg['sandbox-notice-testcases-run-blurb'] -- cfg['sandbox-notice-testcases-run-link-display'] -- cfg['sandbox-notice-testcases-blurb'] is a sentence notifying the user that there is a test cases page -- corresponding to this sandbox that they can edit. $1 is a link to the test cases page. -- cfg['sandbox-notice-testcases-link-display'] is the display value for that link. -- cfg['sandbox-notice-testcases-run-blurb'] is a sentence notifying the user that there is a test cases page -- corresponding to this sandbox that they can edit, along with a link to run it. $1 is a link to the test -- cases page, and $2 is a link to the page to run it. -- cfg['sandbox-notice-testcases-run-link-display'] is the display value for the link to run the test -- cases. --]] cfg['sandbox-notice-testcases-blurb'] = 'See also the companion subpage for $1.' cfg['sandbox-notice-testcases-link-display'] = 'test cases' cfg['sandbox-notice-testcases-run-blurb'] = 'See also the companion subpage for $1 ($2).' cfg['sandbox-notice-testcases-run-link-display'] = 'run' -- cfg['sandbox-category'] -- A category to add to all template sandboxes. cfg['sandbox-category'] = 'Template sandboxes' ---------------------------------------------------------------------------------------------------- -- Start box configuration ---------------------------------------------------------------------------------------------------- -- cfg['documentation-icon-wikitext'] -- The wikitext for the icon shown at the top of the template. cfg['documentation-icon-wikitext'] = '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]' -- cfg['template-namespace-heading'] -- The heading shown in the template namespace. cfg['template-namespace-heading'] = 'Template documentation' -- cfg['module-namespace-heading'] -- The heading shown in the module namespace. cfg['module-namespace-heading'] = 'Module documentation' -- cfg['file-namespace-heading'] -- The heading shown in the file namespace. cfg['file-namespace-heading'] = 'Summary' -- cfg['other-namespaces-heading'] -- The heading shown in other namespaces. cfg['other-namespaces-heading'] = 'Documentation' -- cfg['view-link-display'] -- The text to display for "view" links. cfg['view-link-display'] = 'view' -- cfg['edit-link-display'] -- The text to display for "edit" links. cfg['edit-link-display'] = 'edit' -- cfg['history-link-display'] -- The text to display for "history" links. cfg['history-link-display'] = 'history' -- cfg['purge-link-display'] -- The text to display for "purge" links. cfg['purge-link-display'] = 'purge' -- cfg['create-link-display'] -- The text to display for "create" links. cfg['create-link-display'] = 'create' ---------------------------------------------------------------------------------------------------- -- Link box (end box) configuration ---------------------------------------------------------------------------------------------------- -- cfg['transcluded-from-blurb'] -- Notice displayed when the docs are transcluded from another page. $1 is a wikilink to that page. cfg['transcluded-from-blurb'] = 'The above [[Wikipedia:Template documentation|documentation]] is [[Help:Transclusion|transcluded]] from $1.' --[[ -- cfg['create-module-doc-blurb'] -- Notice displayed in the module namespace when the documentation subpage does not exist. -- $1 is a link to create the documentation page with the preload cfg['module-preload'] and the -- display cfg['create-link-display']. --]] cfg['create-module-doc-blurb'] = 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].' ---------------------------------------------------------------------------------------------------- -- Experiment blurb configuration ---------------------------------------------------------------------------------------------------- --[[ -- cfg['experiment-blurb-template'] -- cfg['experiment-blurb-module'] -- The experiment blurb is the text inviting editors to experiment in sandbox and test cases pages. -- It is only shown in the template and module namespaces. With the default English settings, it -- might look like this: -- -- Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages. -- -- In this example, "sandbox", "edit", "diff", "testcases", and "edit" would all be links. -- -- There are two versions, cfg['experiment-blurb-template'] and cfg['experiment-blurb-module'], depending -- on what namespace we are in. -- -- Parameters: -- -- $1 is a link to the sandbox page. If the sandbox exists, it is in the following format: -- -- cfg['sandbox-link-display'] (cfg['sandbox-edit-link-display'] | cfg['compare-link-display']) -- -- If the sandbox doesn't exist, it is in the format: -- -- cfg['sandbox-link-display'] (cfg['sandbox-create-link-display'] | cfg['mirror-link-display']) -- -- The link for cfg['sandbox-create-link-display'] link preloads the page with cfg['template-sandbox-preload'] -- or cfg['module-sandbox-preload'], depending on the current namespace. The link for cfg['mirror-link-display'] -- loads a default edit summary of cfg['mirror-edit-summary']. -- -- $2 is a link to the test cases page. If the test cases page exists, it is in the following format: -- -- cfg['testcases-link-display'] (cfg['testcases-edit-link-display'] | cfg['testcases-run-link-display']) -- -- If the test cases page doesn't exist, it is in the format: -- -- cfg['testcases-link-display'] (cfg['testcases-create-link-display']) -- -- If the test cases page doesn't exist, the link for cfg['testcases-create-link-display'] preloads the -- page with cfg['template-testcases-preload'] or cfg['module-testcases-preload'], depending on the current -- namespace. --]] cfg['experiment-blurb-template'] = "Editors can experiment in this template's $1 and $2 pages." cfg['experiment-blurb-module'] = "Editors can experiment in this module's $1 and $2 pages." ---------------------------------------------------------------------------------------------------- -- Sandbox link configuration ---------------------------------------------------------------------------------------------------- -- cfg['sandbox-subpage'] -- The name of the template subpage typically used for sandboxes. cfg['sandbox-subpage'] = 'sandbox' -- cfg['template-sandbox-preload'] -- Preload file for template sandbox pages. cfg['template-sandbox-preload'] = 'Template:Documentation/preload-sandbox' -- cfg['module-sandbox-preload'] -- Preload file for Lua module sandbox pages. cfg['module-sandbox-preload'] = 'Template:Documentation/preload-module-sandbox' -- cfg['sandbox-link-display'] -- The text to display for "sandbox" links. cfg['sandbox-link-display'] = 'sandbox' -- cfg['sandbox-edit-link-display'] -- The text to display for sandbox "edit" links. cfg['sandbox-edit-link-display'] = 'edit' -- cfg['sandbox-create-link-display'] -- The text to display for sandbox "create" links. cfg['sandbox-create-link-display'] = 'create' -- cfg['compare-link-display'] -- The text to display for "compare" links. cfg['compare-link-display'] = 'diff' -- cfg['mirror-edit-summary'] -- The default edit summary to use when a user clicks the "mirror" link. $1 is a wikilink to the -- template page. cfg['mirror-edit-summary'] = 'Create sandbox version of $1' -- cfg['mirror-link-display'] -- The text to display for "mirror" links. cfg['mirror-link-display'] = 'mirror' -- cfg['mirror-link-preload'] -- The page to preload when a user clicks the "mirror" link. cfg['mirror-link-preload'] = 'Template:Documentation/mirror' ---------------------------------------------------------------------------------------------------- -- Test cases link configuration ---------------------------------------------------------------------------------------------------- -- cfg['testcases-subpage'] -- The name of the template subpage typically used for test cases. cfg['testcases-subpage'] = 'testcases' -- cfg['template-testcases-preload'] -- Preload file for template test cases pages. cfg['template-testcases-preload'] = 'Template:Documentation/preload-testcases' -- cfg['module-testcases-preload'] -- Preload file for Lua module test cases pages. cfg['module-testcases-preload'] = 'Template:Documentation/preload-module-testcases' -- cfg['testcases-link-display'] -- The text to display for "testcases" links. cfg['testcases-link-display'] = 'testcases' -- cfg['testcases-edit-link-display'] -- The text to display for test cases "edit" links. cfg['testcases-edit-link-display'] = 'edit' -- cfg['testcases-run-link-display'] -- The text to display for test cases "run" links. cfg['testcases-run-link-display'] = 'run' -- cfg['testcases-create-link-display'] -- The text to display for test cases "create" links. cfg['testcases-create-link-display'] = 'create' ---------------------------------------------------------------------------------------------------- -- Add categories blurb configuration ---------------------------------------------------------------------------------------------------- --[[ -- cfg['add-categories-blurb'] -- Text to direct users to add categories to the /doc subpage. Not used if the "content" or -- "docname fed" arguments are set, as then it is not clear where to add the categories. $1 is a -- link to the /doc subpage with a display value of cfg['doc-link-display']. --]] cfg['add-categories-blurb'] = 'Add categories to the $1 subpage.' -- cfg['doc-link-display'] -- The text to display when linking to the /doc subpage. cfg['doc-link-display'] = '/doc' ---------------------------------------------------------------------------------------------------- -- Subpages link configuration ---------------------------------------------------------------------------------------------------- --[[ -- cfg['subpages-blurb'] -- The "Subpages of this template" blurb. $1 is a link to the main template's subpages with a -- display value of cfg['subpages-link-display']. In the English version this blurb is simply -- the link followed by a period, and the link display provides the actual text. --]] cfg['subpages-blurb'] = '$1.' --[[ -- cfg['subpages-link-display'] -- The text to display for the "subpages of this page" link. $1 is cfg['template-pagetype'], -- cfg['module-pagetype'] or cfg['default-pagetype'], depending on whether the current page is in -- the template namespace, the module namespace, or another namespace. --]] cfg['subpages-link-display'] = 'Subpages of this $1' -- cfg['template-pagetype'] -- The pagetype to display for template pages. cfg['template-pagetype'] = 'template' -- cfg['module-pagetype'] -- The pagetype to display for Lua module pages. cfg['module-pagetype'] = 'module' -- cfg['default-pagetype'] -- The pagetype to display for pages other than templates or Lua modules. cfg['default-pagetype'] = 'page' ---------------------------------------------------------------------------------------------------- -- Doc link configuration ---------------------------------------------------------------------------------------------------- -- cfg['doc-subpage'] -- The name of the subpage typically used for documentation pages. cfg['doc-subpage'] = 'doc' -- cfg['docpage-preload'] -- Preload file for template documentation pages in all namespaces. cfg['docpage-preload'] = 'Template:Documentation/preload' -- cfg['module-preload'] -- Preload file for Lua module documentation pages. cfg['module-preload'] = 'Template:Documentation/preload-module-doc' ---------------------------------------------------------------------------------------------------- -- HTML and CSS configuration ---------------------------------------------------------------------------------------------------- -- cfg['templatestyles'] -- The name of the TemplateStyles page where CSS is kept. -- Sandbox CSS will be at Module:Documentation/sandbox/styles.css when needed. cfg['templatestyles'] = 'Module:Documentation/styles.css' -- cfg['container'] -- Class which can be used to set flex or grid CSS on the -- two child divs documentation and documentation-metadata cfg['container'] = 'documentation-container' -- cfg['main-div-classes'] -- Classes added to the main HTML "div" tag. cfg['main-div-classes'] = 'documentation' -- cfg['main-div-heading-class'] -- Class for the main heading for templates and modules and assoc. talk spaces cfg['main-div-heading-class'] = 'documentation-heading' -- cfg['start-box-class'] -- Class for the start box cfg['start-box-class'] = 'documentation-startbox' -- cfg['start-box-link-classes'] -- Classes used for the [view][edit][history] or [create] links in the start box. -- mw-editsection-like is per [[Wikipedia:Village pump (technical)/Archive 117]] cfg['start-box-link-classes'] = 'mw-editsection-like plainlinks' -- cfg['end-box-class'] -- Class for the end box. cfg['end-box-class'] = 'documentation-metadata' -- cfg['end-box-plainlinks'] -- Plainlinks cfg['end-box-plainlinks'] = 'plainlinks' -- cfg['toolbar-class'] -- Class added for toolbar links. cfg['toolbar-class'] = 'documentation-toolbar' -- cfg['clear'] -- Just used to clear things. cfg['clear'] = 'documentation-clear' ---------------------------------------------------------------------------------------------------- -- Tracking category configuration ---------------------------------------------------------------------------------------------------- -- cfg['display-strange-usage-category'] -- Set to true to enable output of cfg['strange-usage-category'] if the module is used on a /doc subpage -- or a /testcases subpage. This should be a boolean value (either true or false). cfg['display-strange-usage-category'] = true -- cfg['strange-usage-category'] -- Category to output if cfg['display-strange-usage-category'] is set to true and the module is used on a -- /doc subpage or a /testcases subpage. cfg['strange-usage-category'] = 'Wikipedia pages with strange ((documentation)) usage' --[[ ---------------------------------------------------------------------------------------------------- -- End configuration -- -- Don't edit anything below this line. ---------------------------------------------------------------------------------------------------- --]] return cfg 71b68ed73088f1a59d61acf06bbee9fde6677f03 Module:Documentation/styles.css 828 362 719 718 2023-07-10T15:44:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Documentation/styles.css]] text text/plain /* {{pp|small=yes}} */ .documentation, .documentation-metadata { border: 1px solid #a2a9b1; background-color: #ecfcf4; clear: both; } .documentation { margin: 1em 0 0 0; padding: 1em; } .documentation-metadata { margin: 0.2em 0; /* same margin left-right as .documentation */ font-style: italic; padding: 0.4em 1em; /* same padding left-right as .documentation */ } .documentation-startbox { padding-bottom: 3px; border-bottom: 1px solid #aaa; margin-bottom: 1ex; } .documentation-heading { font-weight: bold; font-size: 125%; } .documentation-clear { /* Don't want things to stick out where they shouldn't. */ clear: both; } .documentation-toolbar { font-style: normal; font-size: 85%; } ce0e629c92e3d825ab9fd927fe6cc37d9117b6cb Module:Sandbox other 828 363 721 720 2023-07-10T15:44:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Sandbox_other]] wikitext text/x-wiki {{#if:{{#ifeq:{{#invoke:String|sublength|s={{SUBPAGENAME}}|i=0|len=7}}|sandbox|1}}{{#ifeq:{{SUBPAGENAME}}|doc|1}}{{#invoke:String|match|{{PAGENAME}}|/sandbox/styles.css$|plain=false|nomatch=}}|{{{1|}}}|{{{2|}}}}}<!-- --><noinclude>{{documentation}}</noinclude> 91e4ae891d6b791615152c1fbc971414961ba872 Module:High-use 828 364 723 722 2023-07-10T15:44:44Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:High-use]] Scribunto text/plain local p = {} -- _fetch looks at the "demo" argument. local _fetch = require('Module:Transclusion_count').fetch local yesno = require('Module:Yesno') function p.num(frame, count) if count == nil then if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end end -- Build output string local return_value = "" if count == nil then if frame.args[1] == "risk" then return_value = "a very large number of" else return_value = "many" end else -- Use 2 significant figures for smaller numbers and 3 for larger ones local sigfig = 2 if count >= 100000 then sigfig = 3 end -- Prepare to round to appropriate number of sigfigs local f = math.floor(math.log10(count)) - sigfig + 1 -- Round and insert "approximately" or "+" when appropriate if (frame.args[2] == "yes") or (mw.ustring.sub(frame.args[1],-1) == "+") then -- Round down return_value = string.format("%s+", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) ) * (10^(f))) ) else -- Round to nearest return_value = string.format("approximately&#x20;%s", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) + 0.5) * (10^(f))) ) end -- Insert percentage of pages if that is likely to be >= 1% and when |no-percent= not set to yes if count and count > 250000 and not yesno (frame:getParent().args['no-percent']) then local percent = math.floor( ( (count/frame:callParserFunction('NUMBEROFPAGES', 'R') ) * 100) + 0.5) if percent >= 1 then return_value = string.format("%s&#x20;pages, or roughly %s%% of all", return_value, percent) end end end return return_value end -- Actions if there is a large (greater than or equal to 100,000) transclusion count function p.risk(frame) local return_value = "" if frame.args[1] == "risk" then return_value = "risk" else local count = _fetch(frame) if count and count >= 100000 then return_value = "risk" end end return return_value end function p.text(frame, count) -- Only show the information about how this template gets updated if someone -- is actually editing the page and maybe trying to update the count. local bot_text = (frame:preprocess("{{REVISIONID}}") == "") and "\n\n----\n'''Preview message''': Transclusion count updated automatically ([[Template:High-use/doc#Technical details|see documentation]])." or '' if count == nil then if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end end local title = mw.title.getCurrentTitle() if title.subpageText == "doc" or title.subpageText == "sandbox" then title = title.basePageTitle end local systemMessages = frame.args['system'] if frame.args['system'] == '' then systemMessages = nil end -- This retrieves the project URL automatically to simplify localiation. local templateCount = ('on [https://linkcount.toolforge.org/index.php?project=%s&page=%s %s pages]'):format( mw.title.getCurrentTitle():fullUrl():gsub('//(.-)/.*', '%1'), mw.uri.encode(title.fullText), p.num(frame, count)) local used_on_text = "'''This " .. (mw.title.getCurrentTitle().namespace == 828 and "Lua module" or "template") .. ' is used '; if systemMessages then used_on_text = used_on_text .. systemMessages .. ((count and count > 2000) and ("''', and " .. templateCount) or ("'''")) else used_on_text = used_on_text .. templateCount .. "'''" end local sandbox_text = ("%s's [[%s/sandbox|/sandbox]] or [[%s/testcases|/testcases]] subpages, or in your own [[%s]]. "):format( (mw.title.getCurrentTitle().namespace == 828 and "module" or "template"), title.fullText, title.fullText, mw.title.getCurrentTitle().namespace == 828 and "Module:Sandbox|module sandbox" or "Wikipedia:User pages#SUB|user subpage" ) local infoArg = frame.args["info"] ~= "" and frame.args["info"] if (systemMessages or frame.args[1] == "risk" or (count and count >= 100000) ) then local info = systemMessages and '.<br/>Changes to it can cause immediate changes to the Wikipedia user interface.' or '.' if infoArg then info = info .. "<br />" .. infoArg end sandbox_text = info .. '<br /> To avoid major disruption' .. (count and count >= 100000 and ' and server load' or '') .. ', any changes should be tested in the ' .. sandbox_text .. 'The tested changes can be added to this page in a single edit. ' else sandbox_text = (infoArg and ('.<br />' .. infoArg .. ' C') or ' and c') .. 'hanges may be widely noticed. Test changes in the ' .. sandbox_text end local discussion_text = systemMessages and 'Please discuss changes ' or 'Consider discussing changes ' if frame.args["2"] and frame.args["2"] ~= "" and frame.args["2"] ~= "yes" then discussion_text = string.format("%sat [[%s]]", discussion_text, frame.args["2"]) else discussion_text = string.format("%son the [[%s|talk page]]", discussion_text, title.talkPageTitle.fullText ) end return used_on_text .. sandbox_text .. discussion_text .. " before implementing them." .. bot_text end function p.main(frame) local count = nil if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end local image = "[[File:Ambox warning yellow.svg|40px|alt=Warning|link=]]" local type_param = "style" local epilogue = '' if frame.args['system'] and frame.args['system'] ~= '' then image = "[[File:Ambox important.svg|40px|alt=Warning|link=]]" type_param = "content" local nocat = frame:getParent().args['nocat'] or frame.args['nocat'] local categorise = (nocat == '' or not yesno(nocat)) if categorise then epilogue = frame:preprocess('{{Sandbox other||{{#switch:{{#invoke:Effective protection level|{{#switch:{{NAMESPACE}}|File=upload|#default=edit}}|{{FULLPAGENAME}}}}|sysop|templateeditor|interfaceadmin=|#default=[[Category:Pages used in system messages needing protection]]}}}}') end elseif (frame.args[1] == "risk" or (count and count >= 100000)) then image = "[[File:Ambox warning orange.svg|40px|alt=Warning|link=]]" type_param = "content" end if frame.args["form"] == "editnotice" then return frame:expandTemplate{ title = 'editnotice', args = { ["image"] = image, ["text"] = p.text(frame, count), ["expiry"] = (frame.args["expiry"] or "") } } .. epilogue else return require('Module:Message box').main('ombox', { type = type_param, image = image, text = p.text(frame, count), expiry = (frame.args["expiry"] or "") }) .. epilogue end end return p 134551888e066954a89c109d2faa8af71a4454a4 Module:Transclusion count 828 365 725 724 2023-07-10T15:44:44Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Transclusion_count]] Scribunto text/plain local p = {} function p.fetch(frame) local template = nil local return_value = nil -- Use demo parameter if it exists, otherswise use current template name local namespace = mw.title.getCurrentTitle().namespace if frame.args["demo"] and frame.args["demo"] ~= "" then template = mw.ustring.gsub(frame.args["demo"],"^[Tt]emplate:","") elseif namespace == 10 then -- Template namespace template = mw.title.getCurrentTitle().text elseif namespace == 828 then -- Module namespace template = (mw.site.namespaces[828].name .. ":" .. mw.title.getCurrentTitle().text) end -- If in template or module namespace, look up count in /data if template ~= nil then namespace = mw.title.new(template, "Template").namespace if namespace == 10 or namespace == 828 then template = mw.ustring.gsub(template, "/doc$", "") -- strip /doc from end template = mw.ustring.gsub(template, "/sandbox$", "") -- strip /sandbox from end local index = mw.ustring.sub(mw.title.new(template).text,1,1) local status, data = pcall(function () return(mw.loadData('Module:Transclusion_count/data/' .. (mw.ustring.find(index, "%a") and index or "other"))) end) if status then return_value = tonumber(data[mw.ustring.gsub(template, " ", "_")]) end end end -- If database value doesn't exist, use value passed to template if return_value == nil and frame.args[1] ~= nil then local arg1=mw.ustring.match(frame.args[1], '[%d,]+') if arg1 and arg1 ~= '' then return_value = tonumber(frame:callParserFunction('formatnum', arg1, 'R')) end end return return_value end -- Tabulate this data for [[Wikipedia:Database reports/Templates transcluded on the most pages]] function p.tabulate(frame) local list = {} for i = 65, 91 do local data = mw.loadData('Module:Transclusion count/data/' .. ((i == 91) and 'other' or string.char(i))) for name, count in pairs(data) do table.insert(list, {mw.title.new(name, "Template").fullText, count}) end end table.sort(list, function(a, b) return (a[2] == b[2]) and (a[1] < b[1]) or (a[2] > b[2]) end) local lang = mw.getContentLanguage(); for i = 1, #list do list[i] = ('|-\n| %d || [[%s]] || %s\n'):format(i, list[i][1]:gsub('_', ' '), lang:formatNum(list[i][2])) end return table.concat(list) end return p 000ef6bcbf7b66e727870b0c300c4009da300513 Module:Lua banner 828 366 727 726 2023-07-10T15:44:45Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Lua_banner]] Scribunto text/plain -- This module implements the {{lua}} template. local yesno = require('Module:Yesno') local mList = require('Module:List') local mTableTools = require('Module:TableTools') local mMessageBox = require('Module:Message box') local p = {} function p.main(frame) local origArgs = frame:getParent().args local args = {} for k, v in pairs(origArgs) do v = v:match('^%s*(.-)%s*$') if v ~= '' then args[k] = v end end return p._main(args) end function p._main(args) local modules = mTableTools.compressSparseArray(args) local box = p.renderBox(modules) local trackingCategories = p.renderTrackingCategories(args, modules) return box .. trackingCategories end function p.renderBox(modules) local boxArgs = {} if #modules < 1 then boxArgs.text = '<strong class="error">Error: no modules specified</strong>' else local moduleLinks = {} for i, module in ipairs(modules) do moduleLinks[i] = string.format('[[:%s]]', module) local maybeSandbox = mw.title.new(module .. '/sandbox') if maybeSandbox.exists then moduleLinks[i] = moduleLinks[i] .. string.format(' ([[:%s|sandbox]])', maybeSandbox.fullText) end end local moduleList = mList.makeList('bulleted', moduleLinks) local title = mw.title.getCurrentTitle() if title.subpageText == "doc" then title = title.basePageTitle end if title.contentModel == "Scribunto" then boxArgs.text = 'This module depends on the following other modules:' .. moduleList else boxArgs.text = 'This template uses [[Wikipedia:Lua|Lua]]:\n' .. moduleList end end boxArgs.type = 'notice' boxArgs.small = true boxArgs.image = '[[File:Lua-Logo.svg|30px|alt=|link=]]' return mMessageBox.main('mbox', boxArgs) end function p.renderTrackingCategories(args, modules, titleObj) if yesno(args.nocat) then return '' end local cats = {} -- Error category if #modules < 1 then cats[#cats + 1] = 'Lua templates with errors' end -- Lua templates category titleObj = titleObj or mw.title.getCurrentTitle() local subpageBlacklist = { doc = true, sandbox = true, sandbox2 = true, testcases = true } if not subpageBlacklist[titleObj.subpageText] then local protCatName if titleObj.namespace == 10 then local category = args.category if not category then local categories = { ['Module:String'] = 'Templates based on the String Lua module', ['Module:Math'] = 'Templates based on the Math Lua module', ['Module:BaseConvert'] = 'Templates based on the BaseConvert Lua module', ['Module:Citation/CS1'] = 'Templates based on the Citation/CS1 Lua module' } category = modules[1] and categories[modules[1]] category = category or 'Lua-based templates' end cats[#cats + 1] = category protCatName = "Templates using under-protected Lua modules" elseif titleObj.namespace == 828 then protCatName = "Modules depending on under-protected modules" end if not args.noprotcat and protCatName then local protLevels = { autoconfirmed = 1, extendedconfirmed = 2, templateeditor = 3, sysop = 4 } local currentProt if titleObj.id ~= 0 then -- id is 0 (page does not exist) if am previewing before creating a template. currentProt = titleObj.protectionLevels["edit"][1] end if currentProt == nil then currentProt = 0 else currentProt = protLevels[currentProt] end for i, module in ipairs(modules) do if module ~= "WP:libraryUtil" then local moduleProt = mw.title.new(module).protectionLevels["edit"][1] if moduleProt == nil then moduleProt = 0 else moduleProt = protLevels[moduleProt] end if moduleProt < currentProt then cats[#cats + 1] = protCatName break end end end end end for i, cat in ipairs(cats) do cats[i] = string.format('[[Category:%s]]', cat) end return table.concat(cats) end return p 03ec1b34a40121efc562c0c64a67ebbf57d56dff Module:Lua 828 367 729 728 2023-07-10T15:44:45Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Lua]] wikitext text/x-wiki <includeonly>{{#invoke:Lua banner|main}}</includeonly><noinclude> {{Lua|Module:Lua banner}} {{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> dba3962144dacd289dbc34f50fbe0a7bf6d7f2f7 Module:Module other 828 368 731 730 2023-07-10T15:44:46Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Module_other]] wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:Module}} | module | other }} }} | module = {{{1|}}} | other | #default = {{{2|}}} }}<!--End switch--><noinclude> {{documentation}} <!-- Add categories to the /doc subpage, not here! --> </noinclude> 503694836c1b07142e63fd35d8be69ec8bb9ffe7 Module:Module rating 828 369 733 732 2023-07-10T15:44:46Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Module_rating]] wikitext text/x-wiki <includeonly>{{#ifeq:{{SUBPAGENAME}}|doc|<!--do not show protection level of the module on the doc page, use the second and optionally third parameter if the doc page is also protected -->{{#if:{{{2|}}}|{{Pp|{{{2}}}|action={{{3|}}}}}}}|{{Module other|{{ombox | type = notice | image = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = [[File:Ambox warning blue construction.svg|40x40px|link=|alt=Pre-alpha]] | alpha | a = [[File:Alpha lowercase.svg|26x26px|link=|alt=Alpha]] | beta | b = [[File:Greek lc beta.svg|40x40px|link=|alt=Beta]] | release | r | general | g = [[File:Green check.svg|40x40px|link=|alt=Ready for use]] | protected | protect | p = [[File:{{#switch:{{#invoke:Effective protection level|edit|{{#switch:{{SUBPAGENAME}}|doc|sandbox={{FULLBASEPAGENAME}}|{{FULLPAGENAME}}}}}}|autoconfirmed=Semi|extendedconfirmed=Extended|accountcreator|templateeditor=Template|#default=Full}}-protection-shackle.svg|40x40px|link=|alt=Protected]] | semiprotected | semiprotect | semi =[[File:Semi-protection-shackle.svg|40x40px|link=|alt=Semi-protected]] }} | style = | textstyle = | text = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = This module is rated as [[:Category:Modules in pre-alpha development|pre-alpha]]. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in pre-alpha development|{{PAGENAME}}]] }} }} | alpha | a = This module is rated as [[:Category:Modules in alpha|alpha]]. It is ready for third-party input, and may be used on a few pages to see if problems arise, but should be watched. Suggestions for new features or changes in their input and output mechanisms are welcome.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in alpha|{{PAGENAME}}]] }} }} | beta | b = This module is rated as [[:Category:Modules in beta|beta]], and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in beta|{{PAGENAME}}]] }} }} | release | r | general | g = This module is rated as [[:Category:Modules for general use|ready for general use]]. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by [[Wikipedia:Template sandbox and test cases|sandbox testing]] rather than repeated trial-and-error editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules for general use|{{PAGENAME}}]] }} }} | protected | protect | p = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[Wikipedia:Protection policy|protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | semiprotected | semiprotect | semi = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[WP:SEMI|semi-protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | #default = {{error|Module rating is invalid or not specified.}} }} }}|{{error|Error: {{tl|Module rating}} must be placed in the Module namespace.}} [[Category:Pages with templates in the wrong namespace]]|demospace={{{demospace|<noinclude>module</noinclude>}}}}}}}</includeonly><noinclude> {{module rating|release|nocat=true|demospace=module}} {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go in Wikidata. --> </noinclude> bbd244b3ea2e13ec4c1c810ae44f2f3789a93efc Module:Used in system 828 370 735 734 2023-07-10T15:44:47Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Used_in_system]] wikitext text/x-wiki {{#invoke:High-use|main|1=|2={{{2|}}}|system={{#if:{{{1|}}}|{{{1}}}|in system messages}}<noinclude>|nocat=true</noinclude>}}<noinclude> {{documentation}}<!-- Add categories and interwikis to the /doc subpage, not here! --> </noinclude> 0abe278369db6cbbe319e7452d7644e27e11c532 Module:Uses TemplateStyles 828 371 737 736 2023-07-10T15:44:47Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Uses_TemplateStyles]] wikitext text/x-wiki <includeonly>{{#invoke:Uses TemplateStyles|main}}</includeonly><noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> 60f2fc73c4d69b292455879f9fcb3c68f6c63c2a 739 737 2023-07-10T15:44:48Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Uses_TemplateStyles]] wikitext text/x-wiki <includeonly>{{#invoke:Uses TemplateStyles|main}}</includeonly><noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> 60f2fc73c4d69b292455879f9fcb3c68f6c63c2a Module:Uses TemplateStyles/config 828 372 741 740 2023-07-10T15:44:48Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Uses_TemplateStyles/config]] Scribunto text/plain local cfg = {} -- Don’t touch this line. -- Subpage blacklist: these subpages will not be categorized (except for the -- error category, which is always added if there is an error). -- For example “Template:Foo/doc” matches the `doc = true` rule, so it will have -- no categories. “Template:Foo” and “Template:Foo/documentation” match no rules, -- so they *will* have categories. All rules should be in the -- ['<subpage name>'] = true, -- format. cfg['subpage_blacklist'] = { ['doc'] = true, ['sandbox'] = true, ['sandbox2'] = true, ['testcases'] = true, } -- Sandbox title: if the stylesheet’s title is <template>/<stylesheet>.css, the -- stylesheet’s sandbox is expected to be at <template>/<sandbox_title>/<stylesheet>.css -- Set to nil to disable sandbox links. cfg['sandbox_title'] = 'sandbox' -- Error category: this category is added if the module call contains errors -- (e.g. no stylesheet listed). A category name without namespace, or nil -- to disable categorization (not recommended). cfg['error_category'] = 'Uses TemplateStyles templates with errors' -- Default category: this category is added if no custom category is specified -- in module/template call. A category name without namespace, or nil -- to disable categorization. cfg['default_category'] = 'Templates using TemplateStyles' -- Protection conflict category: this category is added if the protection level -- of any stylesheet is lower than the protection level of the template. A category name -- without namespace, or nil to disable categorization (not recommended). cfg['protection_conflict_category'] = 'Templates using TemplateStyles with a different protection level' -- Hierarchy of protection levels, used to determine whether one protection level is lower -- than another and thus should populate protection_conflict_category. No protection is treated as zero cfg['protection_hierarchy'] = { autoconfirmed = 1, extendedconfirmed = 2, templateeditor = 3, sysop = 4 } -- Padlock pattern: Lua pattern to search on protected stylesheets for, or nil -- to disable padlock check. cfg['padlock_pattern'] = '{{pp-' -- Missing padlock category: this category is added if a protected stylesheet -- doesn’t contain any padlock template (specified by the above Lua pattern). -- A category name without namespace (no nil allowed) if the pattern is not nil, -- unused (and thus may be nil) otherwise. cfg['missing_padlock_category'] = 'Templates using TemplateStyles without padlocks' return cfg -- Don’t touch this line. 58e7a37c44f6ea3f6b8af54a559d696cc7256493 Module:Message box/ombox.css 828 373 743 742 2023-07-10T15:44:49Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Message_box/ombox.css]] text text/plain /* {{pp|small=y}} */ .ombox { margin: 4px 0; border-collapse: collapse; border: 1px solid #a2a9b1; /* Default "notice" gray */ background-color: #f8f9fa; box-sizing: border-box; } /* For the "small=yes" option. */ .ombox.mbox-small { font-size: 88%; line-height: 1.25em; } .ombox-speedy { border: 2px solid #b32424; /* Red */ background-color: #fee7e6; /* Pink */ } .ombox-delete { border: 2px solid #b32424; /* Red */ } .ombox-content { border: 1px solid #f28500; /* Orange */ } .ombox-style { border: 1px solid #fc3; /* Yellow */ } .ombox-move { border: 1px solid #9932cc; /* Purple */ } .ombox-protection { border: 2px solid #a2a9b1; /* Gray-gold */ } .ombox .mbox-text { border: none; /* @noflip */ padding: 0.25em 0.9em; width: 100%; } .ombox .mbox-image { border: none; /* @noflip */ padding: 2px 0 2px 0.9em; text-align: center; } .ombox .mbox-imageright { border: none; /* @noflip */ padding: 2px 0.9em 2px 0; text-align: center; } /* An empty narrow cell */ .ombox .mbox-empty-cell { border: none; padding: 0; width: 1px; } .ombox .mbox-invalid-type { text-align: center; } @media (min-width: 720px) { .ombox { margin: 4px 10%; } .ombox.mbox-small { /* @noflip */ clear: right; /* @noflip */ float: right; /* @noflip */ margin: 4px 0 4px 1em; width: 238px; } } 8fe3df4bb607e699eab2dbd23bd4a1a446391002 Module:Transclusion count/data/D 828 374 745 744 2023-07-10T15:44:50Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Transclusion_count/data/D]] Scribunto text/plain return { ["D&D"] = 3900, ["D&D_to-do"] = 5600, ["D-da"] = 2600, ["DANFS"] = 8400, ["DC-Comics-trademark-copyright"] = 2100, ["DCS_Sri_Lanka"] = 2100, ["DDR"] = 2800, ["DEC"] = 8700, ["DECADE"] = 282000, ["DEN"] = 6700, ["DEU"] = 17000, ["DMC"] = 59000, ["DMCA"] = 2100000, ["DNB"] = 8800, ["DNB-Portal"] = 2100, ["DNB_portal"] = 3800, ["DNK"] = 7700, ["DNZB"] = 3600, ["DOM"] = 2100, ["DOWs"] = 2600, ["DRV_links"] = 3600, ["DWT"] = 2300, ["DYKC"] = 6700, ["DYKF"] = 2100, ["DYK_blue"] = 3400, ["DYK_checklist"] = 13000, ["DYK_conditions"] = 65000, ["DYK_files"] = 2100, ["DYK_header"] = 33000, ["DYK_nompage_links"] = 77000, ["DYK_talk"] = 102000, ["DYK_talk/date"] = 102000, ["DYK_tools"] = 51000, ["DYK_tools/styles.css"] = 51000, ["DYKfile"] = 12000, ["DZA"] = 2700, ["Dab"] = 18000, ["Dablink"] = 2100, ["Dagger"] = 18000, ["Dashboard.wikiedu.org_assignment"] = 13000, ["Dashboard.wikiedu.org_bibliography/guide"] = 6400, ["Dashboard.wikiedu.org_course_header"] = 6000, ["Dashboard.wikiedu.org_course_header/edit-note"] = 6000, ["Dashboard.wikiedu.org_draft_template/about_this_sandbox"] = 9800, ["Dashboard.wikiedu.org_evaluate_article/guide"] = 7300, ["Dashboard.wikiedu.org_peer_review/guide"] = 12000, ["Dashboard.wikiedu.org_sandbox"] = 92000, ["Dashboard.wikiedu.org_student_editor"] = 79000, ["Dashboard.wikiedu.org_student_program_sandbox"] = 91000, ["Dashboard.wikiedu.org_talk_course_link"] = 81000, ["Dashboard.wikiedu.org_user_talk"] = 2200, ["Date"] = 41000, ["Date-mf"] = 38000, ["Date_table_sorting"] = 39000, ["Dated_maintenance_category"] = 2160000, ["Dated_maintenance_category_(articles)"] = 2100000, ["Dated_maintenance_category_by_type_(articles)"] = 21000, ["Davis_Cup_player"] = 2500, ["Day+1"] = 6900, ["Day-1"] = 8300, ["Dbox"] = 3000, ["Dda"] = 5200, ["Dead_YouTube_link"] = 2700, ["Dead_Youtube_links"] = 2400, ["Dead_link"] = 278000, ["Death-date"] = 11000, ["Death-date_and_age"] = 10000, ["Death_date"] = 9100, ["Death_date_and_age"] = 383000, ["Death_date_and_given_age"] = 2600, ["Death_year_and_age"] = 16000, ["Death_year_category_header"] = 2000, ["Decade"] = 2100, ["Decade_link"] = 32000, ["Decimals"] = 3000, ["Decline"] = 2500, ["Declined"] = 3200, ["Decrease"] = 34000, ["Define"] = 5100, ["Deg2DMS"] = 3400, ["Deletion_review_log_header"] = 5600, ["Deletion_review_log_header/Core"] = 5600, ["Delink"] = 1970000, ["Delink_question_hyphen-minus"] = 293000, ["Delrevxfd"] = 3500, ["Democratic_Party_(US)/meta/shading"] = 15000, ["Description_missing"] = 6800, ["Designation/abbreviation"] = 8100, ["Designation/color"] = 76000, ["Designation/colour"] = 79000, ["Designation/colour2"] = 18000, ["Designation/divbox"] = 29000, ["Designation/text"] = 42000, ["Designation_list"] = 5300, ["Details"] = 4700, ["DetailsLink"] = 5800, ["Detect_singular"] = 198000, ["Deutsche_Bahn_station_codes"] = 2200, ["DfE_performance_tables"] = 4500, ["Diff"] = 30000, ["Diff2"] = 12000, ["Digits"] = 21000, ["Directories_box"] = 3200, ["Disamb"] = 2200, ["Disambig"] = 63000, ["Disambig-Class"] = 12000, ["DisambigProj"] = 12000, ["DisambigProject"] = 150000, ["Disambigproject"] = 5800, ["Disambiguation"] = 217000, ["Disambiguation/cat"] = 216000, ["Disambiguation_page_short_description"] = 351000, ["Discogs_artist"] = 16000, ["Discogs_master"] = 12000, ["Discogs_release"] = 3000, ["Discussion_bottom"] = 12000, ["Discussion_top"] = 12000, ["DisestcatCountry"] = 9500, ["DisestcatCountry/core"] = 9500, ["DisestcatCountryDecade"] = 2600, ["DisestcatUSstate"] = 4900, ["DisestcatUSstate/core"] = 4900, ["Disputed"] = 2200, ["Distinguish"] = 95000, ["Disused_Rail_Start"] = 3900, ["Disused_rail_start"] = 4200, ["Disused_style"] = 4600, ["Div_col"] = 397000, ["Div_col/styles.css"] = 399000, ["Div_col_end"] = 299000, ["Div_col_start"] = 2900, ["Div_end"] = 2700, ["Divbox"] = 337000, ["Divbox/styles.css"] = 360000, ["Dividing_line"] = 4000, ["Dmbox"] = 458000, ["Dmbox/styles.css"] = 458000, ["Do_not_move_to_Commons"] = 16000, ["Doc"] = 3500, ["Documentation"] = 89000, ["Documentation_subpage"] = 95000, ["Dog_opentask"] = 3400, ["Doi"] = 25000, ["Doing"] = 3600, ["Don't_edit_this_line"] = 107000, ["Don't_edit_this_line_always_display"] = 472000, ["Don't_edit_this_line_extinct"] = 472000, ["Don't_edit_this_line_link_target"] = 472000, ["Don't_edit_this_line_link_text"] = 472000, ["Don't_edit_this_line_parent"] = 472000, ["Don't_edit_this_line_rank"] = 472000, ["Don't_edit_this_line_refs"] = 107000, ["Don't_edit_this_line_same_as"] = 472000, ["Done"] = 101000, ["Doppelganger"] = 2800, ["Dot"] = 2700, ["Double+single"] = 2700, ["Double-dagger"] = 19000, ["Dr"] = 3400, ["Dr-logno"] = 3400, ["Dr-make"] = 3400, ["Dr-yr"] = 3400, ["Draft"] = 4200, ["Draft_article"] = 6200, ["Draft_article_check"] = 6300, ["Draft_categories"] = 6900, ["Draft_other"] = 114000, ["Draft_topics"] = 25000, ["Drafts_moved_from_mainspace"] = 11000, ["Draw"] = 3800, ["Draw_key"] = 18000, ["Draw_links"] = 13000, ["Drep"] = 3400, ["Drugbankcite"] = 4300, ["Drugbox"] = 7500, ["Drugs.com"] = 3600, ["Ds/talk_notice"] = 6200, ["Dts"] = 38000, ["Dubious"] = 8400, ["Duck"] = 2900, ["Dummytab"] = 4800, ["Duration"] = 38000, ["Dutch_municipality"] = 2600, ["Dyktalk"] = 42000, ["Dynamic_list"] = 9800, ["Module:DYK_checklist"] = 13000, ["Module:DYK_checklist/data"] = 13000, ["Module:DYK_nompage_links"] = 77000, ["Module:Data"] = 148000, ["Module:Date"] = 1360000, ["Module:DateI18n"] = 65000, ["Module:Date_table_sorting"] = 39000, ["Module:DecodeEncode"] = 114000, ["Module:Delink"] = 2060000, ["Module:Detect_singular"] = 1500000, ["Module:Disambiguation"] = 2840000, ["Module:Distinguish"] = 95000, ["Module:Documentation"] = 135000, ["Module:Documentation/config"] = 135000, ["Module:Documentation/styles.css"] = 134000, ["Module:Draft_topics"] = 25000, ["Module:Duration"] = 237000, } 5fe4d8676a43df3290b3d4ff79f8604c8ae2a930 Module:Documentation/doc 828 375 747 746 2023-07-10T15:44:51Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Documentation/doc]] wikitext text/x-wiki {{used in system}} {{Module rating|protected}} {{Lua|Module:Documentation/config|Module:Arguments|Module:Message box|Module:Module wikitext|Module:Protection banner}} {{Uses TemplateStyles|Module:Documentation/styles.css}} This module displays a blue box containing documentation for [[Help:Template|templates]], [[Wikipedia:Lua|Lua modules]], or other pages. The {{tl|documentation}} template invokes it. == Normal usage == For most uses, you should use the {{tl|documentation}} template; please see that template's page for its usage instructions and parameters. == Use in other modules == To use this module from another Lua module, first load it with <code>require</code>: <syntaxhighlight lang="lua"> local documentation = require('Module:Documentation').main </syntaxhighlight> Then you can simply call it using a table of arguments. <syntaxhighlight lang="lua"> documentation{content = 'Some documentation', ['link box'] = 'My custom link box'} </syntaxhighlight> Please refer to the [[Template:Documentation/doc|template documentation]] for usage instructions and a list of parameters. == Porting to other wikis == The module has a configuration file at [[Module:Documentation/config]] which is intended to allow easy translation and porting to other wikis. Please see the code comments in the config page for instructions. If you have any questions, or you need a feature which is not currently implemented, please leave a message at <span class="plainlinks">[https://en.wikipedia.org/wiki/Template_talk:Documentation Template talk:Documentation]</span><!-- this link uses external link syntax because it is intended to direct users from third-party wikis to the Wikipedia template talk page; in this situation, an internal link would unhelpfully just point to their local template talk page, and the existence of any given interwiki prefix cannot be assumed --> to get the attention of a developer. The messages that need to be customized to display a documentation template/module at the top of module pages are [[MediaWiki:Scribunto-doc-page-show]] and [[MediaWiki:Scribunto-doc-page-does-not-exist]]. 66a5c1cb6e80e28fd79c6c4544ecf09ced7a6360 Template:Ombox 10 376 749 748 2023-07-10T15:47:56Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Ombox]] wikitext text/x-wiki {{#invoke:Message box|ombox}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 0e54065432d540737b9e56c4e3a8e7f74d4534ea Template:Clc 10 377 751 750 2023-07-10T15:47:59Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Clc]] wikitext text/x-wiki #REDIRECT [[Template:Category link with count]] 02280e2ab57b544236e11f913e3759c5781ca9d5 Template:Module other 10 378 753 752 2023-07-10T15:48:00Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Module_other]] wikitext text/x-wiki {{#switch: <!--If no or empty "demospace" parameter then detect namespace--> {{#if:{{{demospace|}}} | {{lc: {{{demospace}}} }} <!--Use lower case "demospace"--> | {{#ifeq:{{NAMESPACE}}|{{ns:Module}} | module | other }} }} | module = {{{1|}}} | other | #default = {{{2|}}} }}<!--End switch--><noinclude> {{documentation}} <!-- Add categories to the /doc subpage, not here! --> </noinclude> 503694836c1b07142e63fd35d8be69ec8bb9ffe7 Template:Module rating 10 379 755 754 2023-07-10T15:48:01Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Module_rating]] wikitext text/x-wiki <includeonly>{{#ifeq:{{SUBPAGENAME}}|doc|<!--do not show protection level of the module on the doc page, use the second and optionally third parameter if the doc page is also protected -->{{#if:{{{2|}}}|{{Pp|{{{2}}}|action={{{3|}}}}}}}|{{Module other|{{ombox | type = notice | image = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = [[File:Ambox warning blue construction.svg|40x40px|link=|alt=Pre-alpha]] | alpha | a = [[File:Alpha lowercase.svg|26x26px|link=|alt=Alpha]] | beta | b = [[File:Greek lc beta.svg|40x40px|link=|alt=Beta]] | release | r | general | g = [[File:Green check.svg|40x40px|link=|alt=Ready for use]] | protected | protect | p = [[File:{{#switch:{{#invoke:Effective protection level|edit|{{#switch:{{SUBPAGENAME}}|doc|sandbox={{FULLBASEPAGENAME}}|{{FULLPAGENAME}}}}}}|autoconfirmed=Semi|extendedconfirmed=Extended|accountcreator|templateeditor=Template|#default=Full}}-protection-shackle.svg|40x40px|link=|alt=Protected]] | semiprotected | semiprotect | semi =[[File:Semi-protection-shackle.svg|40x40px|link=|alt=Semi-protected]] }} | style = | textstyle = | text = {{#switch: {{{1|}}} | pre-alpha | prealpha | pa = This module is rated as [[:Category:Modules in pre-alpha development|pre-alpha]]. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in pre-alpha development|{{PAGENAME}}]] }} }} | alpha | a = This module is rated as [[:Category:Modules in alpha|alpha]]. It is ready for third-party input, and may be used on a few pages to see if problems arise, but should be watched. Suggestions for new features or changes in their input and output mechanisms are welcome.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in alpha|{{PAGENAME}}]] }} }} | beta | b = This module is rated as [[:Category:Modules in beta|beta]], and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules in beta|{{PAGENAME}}]] }} }} | release | r | general | g = This module is rated as [[:Category:Modules for general use|ready for general use]]. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by [[Wikipedia:Template sandbox and test cases|sandbox testing]] rather than repeated trial-and-error editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules for general use|{{PAGENAME}}]] }} }} | protected | protect | p = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[Wikipedia:Protection policy|protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | semiprotected | semiprotect | semi = This module is [[:Category:Modules subject to page protection|subject to page protection]]. It is a [[Wikipedia:High-risk templates|highly visible module]] in use by a very large number of pages, or is [[Wikipedia:Substitution|substituted]] very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is [[WP:SEMI|semi-protected]] from editing.<!-- -->{{#switch: {{SUBPAGENAME}}|doc|sandbox=<!-- No category for /doc or /sandbox subpages --> | {{#ifeq: {{{nocat|}}} | true | <!-- No category if user sets nocat=true --> | [[Category:Modules subject to page protection|{{PAGENAME}}]] }} }} | #default = {{error|Module rating is invalid or not specified.}} }} }}|{{error|Error: {{tl|Module rating}} must be placed in the Module namespace.}} [[Category:Pages with templates in the wrong namespace]]|demospace={{{demospace|<noinclude>module</noinclude>}}}}}}}</includeonly><noinclude> {{module rating|release|nocat=true|demospace=module}} {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go in Wikidata. --> </noinclude> bbd244b3ea2e13ec4c1c810ae44f2f3789a93efc Module:Infobox 828 381 758 757 2023-07-10T15:48:01Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Infobox]] Scribunto text/plain local p = {} local args = {} local origArgs = {} local root local empty_row_categories = {} local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]' local has_rows = false local lists = { plainlist_t = { patterns = { '^plainlist$', '%splainlist$', '^plainlist%s', '%splainlist%s' }, found = false, styles = 'Plainlist/styles.css' }, hlist_t = { patterns = { '^hlist$', '%shlist$', '^hlist%s', '%shlist%s' }, found = false, styles = 'Hlist/styles.css' } } local function has_list_class(args_to_check) for _, list in pairs(lists) do if not list.found then for _, arg in pairs(args_to_check) do for _, pattern in ipairs(list.patterns) do if mw.ustring.find(arg or '', pattern) then list.found = true break end end if list.found then break end end end end end local function fixChildBoxes(sval, tt) local function notempty( s ) return s and s:match( '%S' ) end if notempty(sval) then local marker = '<span class=special_infobox_marker>' local s = sval -- start moving templatestyles and categories inside of table rows local slast = '' while slast ~= s do slast = s s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1') s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1') end -- end moving templatestyles and categories inside of table rows s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1') s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker) if s:match(marker) then s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '') s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1') s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1') s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1') s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1') s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1') s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1') s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1') s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1') end if s:match(marker) then local subcells = mw.text.split(s, marker) s = '' for k = 1, #subcells do if k == 1 then s = s .. subcells[k] .. '</' .. tt .. '></tr>' elseif k == #subcells then local rowstyle = ' style="display:none"' if notempty(subcells[k]) then rowstyle = '' end s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k] elseif notempty(subcells[k]) then if (k % 2) == 0 then s = s .. subcells[k] else s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>' end end end end -- the next two lines add a newline at the end of lists for the PHP parser -- [[Special:Diff/849054481]] -- remove when [[:phab:T191516]] is fixed or OBE s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n') s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n') s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1') s = mw.ustring.gsub(s, '^(%{%|)', '\n%1') return s else return sval end end -- Cleans empty tables local function cleanInfobox() root = tostring(root) if has_rows == false then root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '') end end -- Returns the union of the values of two tables, as a sequence. local function union(t1, t2) local vals = {} for k, v in pairs(t1) do vals[v] = true end for k, v in pairs(t2) do vals[v] = true end local ret = {} for k, v in pairs(vals) do table.insert(ret, k) end return ret end -- Returns a table containing the numbers of the arguments that exist -- for the specified prefix. For example, if the prefix was 'data', and -- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}. local function getArgNums(prefix) local nums = {} for k, v in pairs(args) do local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$') if num then table.insert(nums, tonumber(num)) end end table.sort(nums) return nums end -- Adds a row to the infobox, with either a header cell -- or a label/data cell combination. local function addRow(rowArgs) if rowArgs.header and rowArgs.header ~= '_BLANK_' then has_rows = true has_list_class({ rowArgs.rowclass, rowArgs.class, args.headerclass }) root :tag('tr') :addClass(rowArgs.rowclass) :cssText(rowArgs.rowstyle) :tag('th') :attr('colspan', '2') :addClass('infobox-header') :addClass(rowArgs.class) :addClass(args.headerclass) -- @deprecated next; target .infobox-<name> .infobox-header :cssText(args.headerstyle) :cssText(rowArgs.rowcellstyle) :wikitext(fixChildBoxes(rowArgs.header, 'th')) if rowArgs.data then root:wikitext( '[[Category:Pages using infobox templates with ignored data cells]]' ) end elseif rowArgs.data and rowArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then has_rows = true has_list_class({ rowArgs.rowclass, rowArgs.class }) local row = root:tag('tr') row:addClass(rowArgs.rowclass) row:cssText(rowArgs.rowstyle) if rowArgs.label then row :tag('th') :attr('scope', 'row') :addClass('infobox-label') -- @deprecated next; target .infobox-<name> .infobox-label :cssText(args.labelstyle) :cssText(rowArgs.rowcellstyle) :wikitext(rowArgs.label) :done() end local dataCell = row:tag('td') dataCell :attr('colspan', not rowArgs.label and '2' or nil) :addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data') :addClass(rowArgs.class) -- @deprecated next; target .infobox-<name> .infobox(-full)-data :cssText(rowArgs.datastyle) :cssText(rowArgs.rowcellstyle) :wikitext(fixChildBoxes(rowArgs.data, 'td')) else table.insert(empty_row_categories, rowArgs.data or '') end end local function renderTitle() if not args.title then return end has_rows = true has_list_class({args.titleclass}) root :tag('caption') :addClass('infobox-title') :addClass(args.titleclass) -- @deprecated next; target .infobox-<name> .infobox-title :cssText(args.titlestyle) :wikitext(args.title) end local function renderAboveRow() if not args.above then return end has_rows = true has_list_class({ args.aboveclass }) root :tag('tr') :tag('th') :attr('colspan', '2') :addClass('infobox-above') :addClass(args.aboveclass) -- @deprecated next; target .infobox-<name> .infobox-above :cssText(args.abovestyle) :wikitext(fixChildBoxes(args.above,'th')) end local function renderBelowRow() if not args.below then return end has_rows = true has_list_class({ args.belowclass }) root :tag('tr') :tag('td') :attr('colspan', '2') :addClass('infobox-below') :addClass(args.belowclass) -- @deprecated next; target .infobox-<name> .infobox-below :cssText(args.belowstyle) :wikitext(fixChildBoxes(args.below,'td')) end local function addSubheaderRow(subheaderArgs) if subheaderArgs.data and subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then has_rows = true has_list_class({ subheaderArgs.rowclass, subheaderArgs.class }) local row = root:tag('tr') row:addClass(subheaderArgs.rowclass) local dataCell = row:tag('td') dataCell :attr('colspan', '2') :addClass('infobox-subheader') :addClass(subheaderArgs.class) :cssText(subheaderArgs.datastyle) :cssText(subheaderArgs.rowcellstyle) :wikitext(fixChildBoxes(subheaderArgs.data, 'td')) else table.insert(empty_row_categories, subheaderArgs.data or '') end end local function renderSubheaders() if args.subheader then args.subheader1 = args.subheader end if args.subheaderrowclass then args.subheaderrowclass1 = args.subheaderrowclass end local subheadernums = getArgNums('subheader') for k, num in ipairs(subheadernums) do addSubheaderRow({ data = args['subheader' .. tostring(num)], -- @deprecated next; target .infobox-<name> .infobox-subheader datastyle = args.subheaderstyle, rowcellstyle = args['subheaderstyle' .. tostring(num)], class = args.subheaderclass, rowclass = args['subheaderrowclass' .. tostring(num)] }) end end local function addImageRow(imageArgs) if imageArgs.data and imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then has_rows = true has_list_class({ imageArgs.rowclass, imageArgs.class }) local row = root:tag('tr') row:addClass(imageArgs.rowclass) local dataCell = row:tag('td') dataCell :attr('colspan', '2') :addClass('infobox-image') :addClass(imageArgs.class) :cssText(imageArgs.datastyle) :wikitext(fixChildBoxes(imageArgs.data, 'td')) else table.insert(empty_row_categories, imageArgs.data or '') end end local function renderImages() if args.image then args.image1 = args.image end if args.caption then args.caption1 = args.caption end local imagenums = getArgNums('image') for k, num in ipairs(imagenums) do local caption = args['caption' .. tostring(num)] local data = mw.html.create():wikitext(args['image' .. tostring(num)]) if caption then data :tag('div') :addClass('infobox-caption') -- @deprecated next; target .infobox-<name> .infobox-caption :cssText(args.captionstyle) :wikitext(caption) end addImageRow({ data = tostring(data), -- @deprecated next; target .infobox-<name> .infobox-image datastyle = args.imagestyle, class = args.imageclass, rowclass = args['imagerowclass' .. tostring(num)] }) end end -- When autoheaders are turned on, preprocesses the rows local function preprocessRows() if not args.autoheaders then return end local rownums = union(getArgNums('header'), getArgNums('data')) table.sort(rownums) local lastheader for k, num in ipairs(rownums) do if args['header' .. tostring(num)] then if lastheader then args['header' .. tostring(lastheader)] = nil end lastheader = num elseif args['data' .. tostring(num)] and args['data' .. tostring(num)]:gsub( category_in_empty_row_pattern, '' ):match('^%S') then local data = args['data' .. tostring(num)] if data:gsub(category_in_empty_row_pattern, ''):match('%S') then lastheader = nil end end end if lastheader then args['header' .. tostring(lastheader)] = nil end end -- Gets the union of the header and data argument numbers, -- and renders them all in order local function renderRows() local rownums = union(getArgNums('header'), getArgNums('data')) table.sort(rownums) for k, num in ipairs(rownums) do addRow({ header = args['header' .. tostring(num)], label = args['label' .. tostring(num)], data = args['data' .. tostring(num)], datastyle = args.datastyle, class = args['class' .. tostring(num)], rowclass = args['rowclass' .. tostring(num)], -- @deprecated next; target .infobox-<name> rowclass rowstyle = args['rowstyle' .. tostring(num)], rowcellstyle = args['rowcellstyle' .. tostring(num)] }) end end local function renderNavBar() if not args.name then return end has_rows = true root :tag('tr') :tag('td') :attr('colspan', '2') :addClass('infobox-navbar') :wikitext(require('Module:Navbar')._navbar{ args.name, mini = 1, }) end local function renderItalicTitle() local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title']) if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then root:wikitext(require('Module:Italic title')._main({})) end end -- Categories in otherwise empty rows are collected in empty_row_categories. -- This function adds them to the module output. It is not affected by -- args.decat because this module should not prevent module-external categories -- from rendering. local function renderEmptyRowCategories() for _, s in ipairs(empty_row_categories) do root:wikitext(s) end end -- Render tracking categories. args.decat == turns off tracking categories. local function renderTrackingCategories() if args.decat == 'yes' then return end if args.child == 'yes' then if args.title then root:wikitext( '[[Category:Pages using embedded infobox templates with the title parameter]]' ) end elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then root:wikitext('[[Category:Articles using infobox templates with no data rows]]') end end --[=[ Loads the templatestyles for the infobox. TODO: FINISH loading base templatestyles here rather than in MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :). When we do this we should clean up the inline CSS below too. Will have to do some bizarre conversion category like with sidebar. ]=] local function loadTemplateStyles() local frame = mw.getCurrentFrame() local hlist_templatestyles = '' if lists.hlist_t.found then hlist_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = lists.hlist_t.styles } } end local plainlist_templatestyles = '' if lists.plainlist_t.found then plainlist_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = lists.plainlist_t.styles } } end -- See function description local base_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' } } local templatestyles = '' if args['templatestyles'] then templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['templatestyles'] } } end local child_templatestyles = '' if args['child templatestyles'] then child_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['child templatestyles'] } } end local grandchild_templatestyles = '' if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['grandchild templatestyles'] } } end return table.concat({ -- hlist -> plainlist -> base is best-effort to preserve old Common.css ordering. -- this ordering is not a guarantee because the rows of interest invoking -- each class may not be on a specific page hlist_templatestyles, plainlist_templatestyles, base_templatestyles, templatestyles, child_templatestyles, grandchild_templatestyles }) end -- common functions between the child and non child cases local function structure_infobox_common() renderSubheaders() renderImages() preprocessRows() renderRows() renderBelowRow() renderNavBar() renderItalicTitle() renderEmptyRowCategories() renderTrackingCategories() cleanInfobox() end -- Specify the overall layout of the infobox, with special settings if the -- infobox is used as a 'child' inside another infobox. local function _infobox() if args.child ~= 'yes' then root = mw.html.create('table') root :addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox') :addClass(args.bodyclass) -- @deprecated next; target .infobox-<name> :cssText(args.bodystyle) has_list_class({ args.bodyclass }) renderTitle() renderAboveRow() else root = mw.html.create() root :wikitext(args.title) end structure_infobox_common() return loadTemplateStyles() .. root end -- If the argument exists and isn't blank, add it to the argument table. -- Blank arguments are treated as nil to match the behaviour of ParserFunctions. local function preprocessSingleArg(argName) if origArgs[argName] and origArgs[argName] ~= '' then args[argName] = origArgs[argName] end end -- Assign the parameters with the given prefixes to the args table, in order, in -- batches of the step size specified. This is to prevent references etc. from -- appearing in the wrong order. The prefixTable should be an array containing -- tables, each of which has two possible fields, a "prefix" string and a -- "depend" table. The function always parses parameters containing the "prefix" -- string, but only parses parameters in the "depend" table if the prefix -- parameter is present and non-blank. local function preprocessArgs(prefixTable, step) if type(prefixTable) ~= 'table' then error("Non-table value detected for the prefix table", 2) end if type(step) ~= 'number' then error("Invalid step value detected", 2) end -- Get arguments without a number suffix, and check for bad input. for i,v in ipairs(prefixTable) do if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then error('Invalid input detected to preprocessArgs prefix table', 2) end preprocessSingleArg(v.prefix) -- Only parse the depend parameter if the prefix parameter is present -- and not blank. if args[v.prefix] and v.depend then for j, dependValue in ipairs(v.depend) do if type(dependValue) ~= 'string' then error('Invalid "depend" parameter value detected in preprocessArgs') end preprocessSingleArg(dependValue) end end end -- Get arguments with number suffixes. local a = 1 -- Counter variable. local moreArgumentsExist = true while moreArgumentsExist == true do moreArgumentsExist = false for i = a, a + step - 1 do for j,v in ipairs(prefixTable) do local prefixArgName = v.prefix .. tostring(i) if origArgs[prefixArgName] then -- Do another loop if any arguments are found, even blank ones. moreArgumentsExist = true preprocessSingleArg(prefixArgName) end -- Process the depend table if the prefix argument is present -- and not blank, or we are processing "prefix1" and "prefix" is -- present and not blank, and if the depend table is present. if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then for j,dependValue in ipairs(v.depend) do local dependArgName = dependValue .. tostring(i) preprocessSingleArg(dependArgName) end end end end a = a + step end end -- Parse the data parameters in the same order that the old {{infobox}} did, so -- that references etc. will display in the expected places. Parameters that -- depend on another parameter are only processed if that parameter is present, -- to avoid phantom references appearing in article reference lists. local function parseDataParameters() preprocessSingleArg('autoheaders') preprocessSingleArg('child') preprocessSingleArg('bodyclass') preprocessSingleArg('subbox') preprocessSingleArg('bodystyle') preprocessSingleArg('title') preprocessSingleArg('titleclass') preprocessSingleArg('titlestyle') preprocessSingleArg('above') preprocessSingleArg('aboveclass') preprocessSingleArg('abovestyle') preprocessArgs({ {prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}} }, 10) preprocessSingleArg('subheaderstyle') preprocessSingleArg('subheaderclass') preprocessArgs({ {prefix = 'image', depend = {'caption', 'imagerowclass'}} }, 10) preprocessSingleArg('captionstyle') preprocessSingleArg('imagestyle') preprocessSingleArg('imageclass') preprocessArgs({ {prefix = 'header'}, {prefix = 'data', depend = {'label'}}, {prefix = 'rowclass'}, {prefix = 'rowstyle'}, {prefix = 'rowcellstyle'}, {prefix = 'class'} }, 50) preprocessSingleArg('headerclass') preprocessSingleArg('headerstyle') preprocessSingleArg('labelstyle') preprocessSingleArg('datastyle') preprocessSingleArg('below') preprocessSingleArg('belowclass') preprocessSingleArg('belowstyle') preprocessSingleArg('name') -- different behaviour for italics if blank or absent args['italic title'] = origArgs['italic title'] preprocessSingleArg('decat') preprocessSingleArg('templatestyles') preprocessSingleArg('child templatestyles') preprocessSingleArg('grandchild templatestyles') end -- If called via #invoke, use the args passed into the invoking template. -- Otherwise, for testing purposes, assume args are being passed directly in. function p.infobox(frame) if frame == mw.getCurrentFrame() then origArgs = frame:getParent().args else origArgs = frame end parseDataParameters() return _infobox() end -- For calling via #invoke within a template function p.infoboxTemplate(frame) origArgs = {} for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end parseDataParameters() return _infobox() end return p 0ddb7e5c8426d67cd589b710efb9912ddfb67fea Template:Uses TemplateStyles 10 380 759 756 2023-07-10T15:48:01Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Uses_TemplateStyles]] wikitext text/x-wiki <includeonly>{{#invoke:Uses TemplateStyles|main}}</includeonly><noinclude>{{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> 60f2fc73c4d69b292455879f9fcb3c68f6c63c2a Template:Infobox/doc 10 338 761 672 2023-07-10T15:48:02Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Infobox/doc]] wikitext text/x-wiki {{Documentation subpage}} <!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) --> {{distinguish|Template:Userbox}} {{#ifeq:{{SUBPAGENAME}}|sandbox||{{High-use}}}} {{Lua|Module:Infobox}} {{Parameter names example |name={{PAGENAME}} <!--|child |subbox |decat--> |title |above |subheader |subheader1 |subheader2={{{subheader2}}}<br/>...... |image|caption |image1|caption1 |image2|caption2={{{caption2}}}<br/>...... |header1=<div style="border-top:1px dashed #ccc;">{{{header1}}}<br/>{{nobold|( ''or'' )}}</div> |label2={{{label1}}} |data2={{{data1}}} |data3=( ''or'' ) |data4=<div style="padding-bottom:0.25em;border-bottom:1px dashed #ccc;">{{{data1}}}</div> |header5={{{header2}}}<br/><div style="padding:0.75em 0 0.5em;">{{nobold|( ''or'' )}}</div> |label6={{{label2}}} |data6={{{data2}}} |data7=( ''or'' ) |data8=<div style="padding-bottom:0.25em;border-bottom:1px dashed #ccc;">{{{data2}}}</div> |data9=<div style="padding:0.75em 0 0.5em;">( ''etc'' )</div> |below }} This template is intended as a meta template: a template used for constructing other templates. '''Note''': In general, it is not meant for use directly in an article, but can be used on a one-off basis if required. [[Help:Infobox]] contains an introduction about the recommended content and design of infoboxes; [[Wikipedia:Manual of Style/Infoboxes]] contains additional style guidelines. See [[WP:List of infoboxes]] and [[:Category:Infobox templates]] for lists of prepared topic-specific infoboxes. == Usage == {{tlf|Infobox}} is a meta-template: used to organise an actual <nowiki>{{Infobox sometopic}}</nowiki> template (like {{tl|Infobox building}}). For <code><nowiki>[[Template:Infobox sometopic]]</nowiki></code>, template code then looks like this, simplified: <syntaxhighlight lang="wikitext"> {{Infobox | name = {{{name|{{PAGENAME}}}}} | image = {{{image|}}} | caption1 = {{{caption|}}} | label1 = Former names | data1 = {{{former_names|}}} | header2 = General information | label3 = Status | data3 = {{{status|}}} ... <!-- etc. --> }} </syntaxhighlight> == Optional control parameters == ; name : If this parameter is present, "view/talk/edit" links will be added to the bottom of the infobox pointing to the named page, prefixed by <code>Template:</code> if no namespace is specified. You may use the value <nowiki>{{subst:PAGENAME}}</nowiki>; however, this is rarely what you want because it will send users clicking these links in an infobox to the template code rather than the data in the infobox they probably want to change. ; child : See the [[#Embedding|Embedding]] section for details. If this is set to "yes", this child infobox should be titled but have no name parameter. This parameter is empty by default, set it to "yes" to activate it. ; subbox : See the [[#Subboxes|Subboxes]] section for details. If this is set to "yes", this subbox should be titled but have no name parameter. This parameter is empty by default, set to "yes" to activate it. It has no effect if the '''child''' parameter is also set to "yes". ; decat : If this is set to "yes", the current page will not be autocategorized in a maintenance category when the generated infobox has some problems or no visible data section. Leave empty by default or set to "yes" to activate it. ; autoheaders: If this is set to any non-blank value, headers which are not followed by data fields are suppressed. See the "[[#Hiding headers when all its data fields are empty|hiding headers when all its data fields are empty]]" section for more details. == Content parameters == === Title === There are two different ways to put a title on an infobox. One contains the title inside the infobox's border in the uppermost cell of the table, the other puts it as a caption on top of the table. You can use them both together, or just one or the other, or neither (though this is not recommended): ; title : Text to put in the caption over the top of the table (or as section header before the whole content of this table, if this is a child infobox). For [[Wikipedia:Manual of Style/Accessibility#Tables|accessibility reasons]], this is the most recommended alternative. ; above : Text to put within the uppermost cell of the table. ; subheader(n) : additional title fields which fit below {{{title}}} and {{{above}}}, but before images. Examples: {{Infobox | name = Infobox/doc | title = Text in caption over infobox | subheader = Subheader of the infobox | header = (the rest of the infobox goes here) }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | title = Text in caption over infobox | subheader = Subheader of the infobox | header = (the rest of the infobox goes here) }} </syntaxhighlight>{{clear}} {{Infobox | name = Infobox/doc | above = Text in uppermost cell of infobox | subheader = Subheader of the infobox | subheader2 = Second subheader of the infobox | header = (the rest of the infobox goes here) }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | above = Text in uppermost cell of infobox | subheader = Subheader of the infobox | subheader2 = Second subheader of the infobox | header = (the rest of the infobox goes here) }} </syntaxhighlight>{{clear}} === Illustration images === ; image(n) : images to display at the top of the template. Use full image syntax, for example <nowiki>[[File:example.png|200px|alt=Example alt text]]</nowiki>. Image is centered by default. See [[WP:ALT]] for more on alt text. ; caption(n) : Text to put underneath the images. === Main data === ; header(n) : Text to use as a header in row n. ; label(n) : Text to use as a label in row n. ; data(n) : Text to display as data in row n. Note: for any given value for (n), not all combinations of parameters are permitted. The presence of a {{para|header''(n)''}} will cause the corresponding {{para|data''(n)''}} (and {{para|rowclass''(n)''}} {{para|label''(n)''}}, see below) to be ignored; the absence of a {{para|data''(n)''}} will cause the corresponding {{para|label''(n)''}} to be ignored. Valid combinations for any single row are: * {{para|class''(n)''}} {{para|header''(n)''}} * {{para|rowclass''(n)''}} {{para|class''(n)''}} {{para|data''(n)''}} * {{para|rowclass''(n)''}} {{para|label''(n)''}} {{para|class''(n)''}} {{para|data''(n)''}} See the rendering of header4, label4, and data4 in the [[#Examples|Examples]] section below. ==== Number ranges ==== To allow flexibility when the layout of an infobox is changed, it may be helpful when developing an infobox to use non-contiguous numbers for header and label/data rows. Parameters for new rows can then be inserted in future without having to renumber existing parameters. For example: <syntaxhighlight lang="wikitext" style="overflow:auto"> | header3 = Section 1 | label5 = Label A | data5 = Data A | label7 = Label C | data7 = Data C | header10 = Section 2 | label12 = Label D | data12 = Data D </syntaxhighlight>{{clear}} It is also possible to automatically renumber parameter names by using [[User:Frietjes/infoboxgap.js]] or [[Module:IncrementParams]]. There is no upper limit on numbers but there must be at most 50 between each used number. ==== Making data fields optional ==== A row with a label but no data is not displayed. This allows for the easy creation of optional infobox content rows. To make a row optional use a parameter that defaults to an empty string, like so: <syntaxhighlight lang="wikitext" style="overflow:auto"> | label5 = Population | data5 = {{{population|}}} </syntaxhighlight>{{clear}} This way if an article doesn't define the population parameter in its infobox the row won't be displayed. For more complex fields with pre-formatted contents that would still be present even if the parameter wasn't set, you can wrap it all in an "#if" statement to make the whole thing vanish when the parameter is not used. For instance, the "#if" statement in the following example reads "#if:the parameter ''mass'' has been supplied |then display it, followed by 'kg'": <syntaxhighlight lang="wikitext" style="overflow:auto"> | label6 = Mass | data6 = {{ #if: {{{mass|}}} | {{{mass}}} kg }} </syntaxhighlight>{{clear}} For more on #if, see [[meta:ParserFunctions##if:|here]]. ==== Hiding headers when all its data fields are empty ==== You can also make headers automatically hide when their section is empty (has no data-row showing). Consider this situation: {{Infobox | title = Example: header with & without data | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} </syntaxhighlight>{{clear}} If you want hide the header when no {{para|data''N''}} values are present, use '''{{para|autoheaders|y}}''': {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = Header5 with data below | label6 = label6 text | data6 = Some value }} </syntaxhighlight>{{clear}} So, header1 will be shown if any of item1, item2, or item3 is defined. If none of the three parameters are defined the header won't be shown and no empty row appears before the next visible content. Note: if the data has empty css elements, like {{para|data|2=&lt;span style="background:yellow;">&lt;/span>}}, this will be treated as non-empty (having data). If {{para|autoheaders|y}} but there are items that you ''do not'' want to trigger a header, place {{para|headerX|_BLANK_}}. This will serve as an empty header and separate it from the subsequent items. {{Infobox | title = Example: blank header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = _BLANK_ | label6 = label6 text | data6 = Some value, but does not trigger header1 or show header5 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Example: header with & without data | autoheaders = y | headerstyle = background:lightgrey | header1 = Header1 with empty section | label2 = label2 text | data2 = | label3 = label3 text | data3 = | label4 = label4 text | data4 = | header5 = _BLANK_ | label6 = label6 text | data6 = Some value, but does not trigger header1 or show header5 }} </syntaxhighlight>{{clear}} === Footer === ; below : Text to put in the bottom cell. The bottom cell is intended for footnotes, see-also, and other such information. == Presentation parameters == === Italic titles === Titles of articles with infoboxes may be made italic, in line with [[WP:ITALICTITLE]], by passing the <code>italic title</code> parameter. * Turn on italic titles by passing {{para|italic title|<nowiki>{{{italic title|}}}</nowiki>}} from the infobox. * Turn off by default (notably because only Latin script may be safely rendered in this style and italic may be needed to distinguish foreign language from local English language only in that script, but would be difficult to read for other scripts) but allow some instances to be made italic by passing {{para|italic title|<nowiki>{{{italic title|no}}}</nowiki>}} * Do not make any titles italic by not passing the parameter at all. === CSS styling === {{div col}} ; bodystyle : Applies to the infobox table as a whole ; titlestyle : Applies only to the title caption. Adding a background color is usually inadvisable since the text is rendered "outside" the infobox. ; abovestyle : Applies only to the "above" cell at the top. The default style has font-size:125%; since this cell is usually used for a title, if you want to use the above cell for regular-sized text include "font-size:100%;" in the abovestyle. ; imagestyle : Applies to the cell the image is in. This includes the text of the image caption, but you should set text properties with captionstyle instead of imagestyle in case the caption is moved out of this cell in the future. ; captionstyle : Applies to the text of the image caption. ; rowstyle(n) : This parameter is inserted into the <code>style</code> attribute for the specified row. ; headerstyle : Applies to all header cells ; subheaderstyle : Applies to all subheader cells ; labelstyle : Applies to all label cells ; datastyle : Applies to all data cells ; belowstyle : Applies only to the below cell {{div col end}} === HTML classes and microformats === {{div col}} ; bodyclass : This parameter is inserted into the <code>class</code> attribute for the infobox as a whole. ; titleclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''title''' caption. <!-- currently not implemented in Lua module ; aboverowclass : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''above''' cell is on. --> ; aboveclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''above''' cell. ; subheaderrowclass(n) : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''subheader''' is on. ; subheaderclass(n) : This parameter is inserted into the <code>class</code> attribute for the infobox's '''subheader'''. ; imagerowclass(n) : These parameters are inserted into the <code>class</code> attribute for the complete table row their respective '''image''' is on. ; imageclass : This parameter is inserted into the <code>class</code> attribute for the '''image'''. ; rowclass(n) : This parameter is inserted into the <code>class</code> attribute for the specified row including the '''label''' and '''data''' cells. ; class(n) : This parameter is inserted into the <code>class</code> attribute for the '''data''' cell of the specified row. If there's no '''data''' cell it has no effect. <!-- currently not implemented in Lua module ; belowrowclass : This parameter is inserted into the <code>class</code> attribute for the complete table row the '''below''' cell is on. --> ; belowclass : This parameter is inserted into the <code>class</code> attribute for the infobox's '''below''' cell. {{div col end}} This template supports the addition of microformat information. This is done by adding "class" attributes to various data cells, indicating what kind of information is contained within. Multiple class names may be specified, separated by spaces, some of them being used as selectors for custom styling according to a project policy or to the skin selected in user preferences, others being used for microformats. To flag an infobox as containing [[hCard]] information, for example, add the following parameter: <syntaxhighlight lang="wikitext" style="overflow:auto"> | bodyclass = vcard </syntaxhighlight>{{clear}} And for each row containing a data cell that's part of the vcard, add a corresponding class parameter: <syntaxhighlight lang="wikitext" style="overflow:auto"> | class1 = fn | class2 = org | class3 = tel </syntaxhighlight>{{clear}} ...and so forth. "above" and "title" can also be given classes, since these are usually used to display the name of the subject of the infobox. See [[Wikipedia:WikiProject Microformats]] for more information on adding microformat information to Wikipedia, and [[microformat]] for more information on microformats in general. == Examples == Notice how the row doesn't appear in the displayed infobox when a '''label''' is defined without an accompanying '''data''' cell, and how all of them are displayed when a '''header''' is defined on the same row as a '''data''' cell. Also notice that '''subheaders''' are not bold by default like the '''headers''' used to split the main data section, because this role is meant to be for the '''above''' cell : {{Infobox |name = Infobox/doc |bodystyle = |titlestyle = |abovestyle = background:#cfc; |subheaderstyle = |title = Test Infobox |above = Above text |subheader = Subheader above image |subheader2 = Second subheader |imagestyle = |captionstyle = |image = [[File:Example-serious.jpg|200px|alt=Example alt text]] |caption = Caption displayed below File:Example-serious.jpg |headerstyle = background:#ccf; |labelstyle = background:#ddf; |datastyle = |header1 = Header defined alone | label1 = | data1 = |header2 = | label2 = Label defined alone does not display (needs data, or is suppressed) | data2 = |header3 = | label3 = | data3 = Data defined alone |header4 = All three defined (header, label, data, all with same number) | label4 = does not display (same number as a header) | data4 = does not display (same number as a header) |header5 = | label5 = Label and data defined (label) | data5 = Label and data defined (data) |belowstyle = background:#ddf; |below = Below text }} <syntaxhighlight lang="wikitext"> {{Infobox |name = Infobox/doc |bodystyle = |titlestyle = |abovestyle = background:#cfc; |subheaderstyle = |title = Test Infobox |above = Above text |subheader = Subheader above image |subheader2 = Second subheader |imagestyle = |captionstyle = |image = [[File:Example-serious.jpg|200px|alt=Example alt text]] |caption = Caption displayed below File:Example-serious.jpg |headerstyle = background:#ccf; |labelstyle = background:#ddf; |datastyle = |header1 = Header defined alone | label1 = | data1 = |header2 = | label2 = Label defined alone does not display (needs data, or is suppressed) | data2 = |header3 = | label3 = | data3 = Data defined alone |header4 = All three defined (header, label, data, all with same number) | label4 = does not display (same number as a header) | data4 = does not display (same number as a header) |header5 = | label5 = Label and data defined (label) | data5 = Label and data defined (data) |belowstyle = background:#ddf; |below = Below text }} </syntaxhighlight> For this example, the {{para|bodystyle}} and {{para|labelstyle}} parameters are used to adjust the infobox width and define a default width for the column of labels: {{Infobox |name = Infobox/doc |bodystyle = width:20em |titlestyle = |title = Test Infobox |headerstyle = |labelstyle = width:33% |datastyle = |header1 = | label1 = Label 1 | data1 = Data 1 |header2 = | label2 = Label 2 | data2 = Data 2 |header3 = | label3 = Label 3 | data3 = Data 3 |header4 = Header 4 | label4 = | data4 = |header5 = | label5 = Label 5 | data5 = Data 5: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |belowstyle = |below = Below text }} <syntaxhighlight lang="wikitext"> {{Infobox |name = Infobox/doc |bodystyle = width:20em |titlestyle = |title = Test Infobox |headerstyle = |labelstyle = width:33% |datastyle = |header1 = | label1 = Label 1 | data1 = Data 1 |header2 = | label2 = Label 2 | data2 = Data 2 |header3 = | label3 = Label 3 | data3 = Data 3 |header4 = Header 4 | label4 = | data4 = |header5 = | label5 = Label 5 | data5 = Data 5: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |belowstyle = |below = Below text }} </syntaxhighlight> == Embedding == <!--Linked from [[Template:Subinfobox bodystyle/doc]]--> One infobox template can be embedded into another using the {{para|child}} parameter. This feature can be used to create a modular infobox, or to create better-defined logical sections. Long ago, it was necessary to use embedding in order to create infoboxes with more than 99 rows; but nowadays there's no limit to the number of rows that can be defined in a single instance of <code><nowiki>{{infobox}}</nowiki></code>. {{Infobox | title = Top level title | data1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | data2 = {{Infobox | decat = yes | child = yes |title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | data1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | data2 = {{Infobox | decat = yes | child = yes |title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} Note, in the examples above, the child infobox is placed in a <code>data</code> field, not a <code>header</code> field. Notice that the section subheadings are not in bold font if bolding is not explicitly specified. To obtain bold section headings, place the child infobox in a '''header''' field (but not in a '''label''' field because it would not be displayed!), either using {{Infobox | title = Top level title | header1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | header2 = {{Infobox | decat = yes | child = yes | title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | header1 = {{Infobox | decat = yes | child = yes | title = First subsection | label1= Label 1.1 | data1 = Data 1.1 }} | header2 = {{Infobox | decat = yes | child = yes | title = Second subsection | label1= Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} or, {{Infobox | title = Top level title | header1 = First subsection {{Infobox | decat = yes | child = yes | label1 = Label 1.1 | data1 = Data 1.1 }} | header2 = Second subsection {{Infobox | decat = yes | child = yes | label1 = Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | title = Top level title | header1 = First subsection {{Infobox | decat = yes | child = yes | label1 = Label 1.1 | data1 = Data 1.1 }} | header2 = Second subsection {{Infobox | decat = yes | child = yes | label1 = Label 2.1 | data1 = Data 2.1 }} | belowstyle = | below = Below text }} </syntaxhighlight>{{clear}} Note that omitting the {{para|title}} parameter, and not including any text preceding the embedded infobox, may result in spurious blank table rows, creating gaps in the visual presentation. The garbage output can be suppressed using {{para|rowstyleN|display: none}}, replacing N with the data/header number. [[Wikipedia:WikiProject Infoboxes/embed]] includes some links to Wikipedia articles which include infoboxes embedded within other infoboxes. == Subboxes == An alternative method for embedding is to use {{para|subbox|yes}}, which removes the outer border from the infobox, but preserves the interior structure. One feature of this approach is that the parent and child boxes need not have the same structure, and the label and data fields are not aligned between the parent and child boxes because they are not in the same parent table. {{Infobox | headerstyle = background-color:#eee; | labelstyle = background-color:#eee; | header1 = Main 1 | header2 = Main 2 | data3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | data4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | label5 = Label 5 | data5 = Data 5 | header6 = Main 6 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | headerstyle = background-color:#eee; | labelstyle = background-color:#eee; | header1 = Main 1 | header2 = Main 2 | data3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | data4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | label5 = Label 5 | data5 = Data 5 | header6 = Main 6 }} </syntaxhighlight>{{clear}} Similar embedding techniques may be used within content parameters of some other templates generating tables (such as [[:Template:Sidebar|Sidebar]]) : {{Sidebar | navbar = off | headingstyle = background-color:#eee; | heading1 = Heading 1 | heading2 = Heading 2 | content3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | content4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | heading5 = Heading 5 }} <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Sidebar | navbar = off | headingstyle = background-color:#eee; | heading1 = Heading 1 | heading2 = Heading 2 | content3 = {{Infobox | subbox = yes | headerstyle = background-color:#ccc; | labelstyle = background-color:#ddd; | header1 = Sub 3-1 | header2 = Sub 3-2 | label3 = Label 3-3 | data3 = Data 3-3 }} | content4 = {{Infobox | subbox = yes | labelstyle = background-color:#ccc; | label1 = Label 4-1 | data1 = Data 4-1 }} | heading5 = Heading 5 }} </syntaxhighlight>{{clear}} Note that the default padding of the parent data cell containing each subbox is still visible, so the subboxes are slightly narrower than the parent box and there's a higher vertical spacing between standard cells of the parent box than between cells of distinct subboxes. == Controlling line-breaking in embedded bulletless lists == Template {{tlx|nbsp}} may be used with {{tlx|wbr}} and {{tlx|nowrap}} to control line-breaking in bulletless lists embedded in infoboxes (e.g. cast list in {{tlx|Infobox film}}), to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details. == Full blank syntax == (Note: there is no limit to the number of possible rows; only 20 are given below since infoboxes larger than that will be relatively rare. Just extend the numbering as needed. The microformat "class" parameters are also omitted as they are not commonly used.) <syntaxhighlight lang="wikitext" style="overflow:auto"> {{Infobox | name = {{subst:PAGENAME}} | child = {{{child|}}} | subbox = {{{subbox|}}} | italic title = {{{italic title|no}}} | templatestyles = | child templatestyles = | grandchild templatestyles = | bodystyle = | titlestyle = | abovestyle = | subheaderstyle = | title = | above = | subheader = | imagestyle = | captionstyle = | image = | caption = | image2 = | caption2 = | headerstyle = | labelstyle = | datastyle = | header1 = | label1 = | data1 = | header2 = | label2 = | data2 = | header3 = | label3 = | data3 = | header4 = | label4 = | data4 = | header5 = | label5 = | data5 = | header6 = | label6 = | data6 = | header7 = | label7 = | data7 = | header8 = | label8 = | data8 = | header9 = | label9 = | data9 = | header10 = | label10 = | data10 = | header11 = | label11 = | data11 = | header12 = | label12 = | data12 = | header13 = | label13 = | data13 = | header14 = | label14 = | data14 = | header15 = | label15 = | data15 = | header16 = | label16 = | data16 = | header17 = | label17 = | data17 = | header18 = | label18 = | data18 = | header19 = | label19 = | data19 = | header20 = | label20 = | data20 = | belowstyle = | below = }} </syntaxhighlight>{{clear}} {{Help:Infobox/user style}} == Porting to other MediaWikis == The infobox template requires the [[:mw:Extension:Scribunto|Scribunto]] extension. [[Wikipedia:WikiProject Transwiki|WikiProject Transwiki]] has a version of this template that has been modified to work on other MediaWikis. == TemplateData == {{TemplateData header}} <templatedata> { "description": "This template is intended as a meta template, a template used for constructing other templates. In general, it is not meant for use directly in an article but can be used on a one-off basis if required.", "format": "{{_\n| ________________ = _\n}}\n", "params": { "title": { "label": "Title", "description": "Title displayed above the infobox", "type": "string", "suggested": true }, "image": { "label": "Image", "description": "Image illustrating the topic. Use full image syntax.", "type": "content", "suggested": true, "example": "[[File:example.png|200px|alt=Example alt text]]" }, "caption": { "label": "Caption", "description": "caption for the image", "type": "content", "suggested": true } }, "paramOrder": [ "title", "image", "caption" ] } </templatedata> ==Tracking categories== * {{Category link with count|Articles with missing Wikidata information}} * {{Category link with count|Articles using infobox templates with no data rows}} * {{Category link with count|Pages using embedded infobox templates with the title parameter}} ==See also== * [[Module:Infobox]], the [[WP:LUA|Lua]] module on which this template is based * [[Module:Check for unknown parameters]] * {{tl|Infobox3cols}} * {{tl|Navbox}} and {{tl|Sidebar}} * [[Wikipedia:List of infoboxes|List of infoboxes]] * [[:Module:InfoboxImage]] <includeonly>{{Sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:Infobox templates| ]] [[Category:Wikipedia metatemplates|Infobox]] [[Category:Templates generating microformats]] [[Category:Templates that add a tracking category]] [[Category:Templates based on the Infobox Lua module]] }}</includeonly> 7b5cc59c733eab17e47789808768a3f1064804b1 Template:Plainlist/styles.css 10 382 763 762 2023-07-10T15:48:03Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Plainlist/styles.css]] text text/plain /* {{pp-template|small=yes}} */ .plainlist ol, .plainlist ul { line-height: inherit; list-style: none; margin: 0; padding: 0; /* Reset Minerva default */ } .plainlist ol li, .plainlist ul li { margin-bottom: 0; } 51706efa229ff8794c0d94f260a208e7c5e6ec30 Module:High-use 828 364 767 723 2023-07-10T15:48:06Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:High-use]] Scribunto text/plain local p = {} -- _fetch looks at the "demo" argument. local _fetch = require('Module:Transclusion_count').fetch local yesno = require('Module:Yesno') function p.num(frame, count) if count == nil then if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end end -- Build output string local return_value = "" if count == nil then if frame.args[1] == "risk" then return_value = "a very large number of" else return_value = "many" end else -- Use 2 significant figures for smaller numbers and 3 for larger ones local sigfig = 2 if count >= 100000 then sigfig = 3 end -- Prepare to round to appropriate number of sigfigs local f = math.floor(math.log10(count)) - sigfig + 1 -- Round and insert "approximately" or "+" when appropriate if (frame.args[2] == "yes") or (mw.ustring.sub(frame.args[1],-1) == "+") then -- Round down return_value = string.format("%s+", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) ) * (10^(f))) ) else -- Round to nearest return_value = string.format("approximately&#x20;%s", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) + 0.5) * (10^(f))) ) end -- Insert percentage of pages if that is likely to be >= 1% and when |no-percent= not set to yes if count and count > 250000 and not yesno (frame:getParent().args['no-percent']) then local percent = math.floor( ( (count/frame:callParserFunction('NUMBEROFPAGES', 'R') ) * 100) + 0.5) if percent >= 1 then return_value = string.format("%s&#x20;pages, or roughly %s%% of all", return_value, percent) end end end return return_value end -- Actions if there is a large (greater than or equal to 100,000) transclusion count function p.risk(frame) local return_value = "" if frame.args[1] == "risk" then return_value = "risk" else local count = _fetch(frame) if count and count >= 100000 then return_value = "risk" end end return return_value end function p.text(frame, count) -- Only show the information about how this template gets updated if someone -- is actually editing the page and maybe trying to update the count. local bot_text = (frame:preprocess("{{REVISIONID}}") == "") and "\n\n----\n'''Preview message''': Transclusion count updated automatically ([[Template:High-use/doc#Technical details|see documentation]])." or '' if count == nil then if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end end local title = mw.title.getCurrentTitle() if title.subpageText == "doc" or title.subpageText == "sandbox" then title = title.basePageTitle end local systemMessages = frame.args['system'] if frame.args['system'] == '' then systemMessages = nil end -- This retrieves the project URL automatically to simplify localiation. local templateCount = ('on [https://linkcount.toolforge.org/index.php?project=%s&page=%s %s pages]'):format( mw.title.getCurrentTitle():fullUrl():gsub('//(.-)/.*', '%1'), mw.uri.encode(title.fullText), p.num(frame, count)) local used_on_text = "'''This " .. (mw.title.getCurrentTitle().namespace == 828 and "Lua module" or "template") .. ' is used '; if systemMessages then used_on_text = used_on_text .. systemMessages .. ((count and count > 2000) and ("''', and " .. templateCount) or ("'''")) else used_on_text = used_on_text .. templateCount .. "'''" end local sandbox_text = ("%s's [[%s/sandbox|/sandbox]] or [[%s/testcases|/testcases]] subpages, or in your own [[%s]]. "):format( (mw.title.getCurrentTitle().namespace == 828 and "module" or "template"), title.fullText, title.fullText, mw.title.getCurrentTitle().namespace == 828 and "Module:Sandbox|module sandbox" or "Wikipedia:User pages#SUB|user subpage" ) local infoArg = frame.args["info"] ~= "" and frame.args["info"] if (systemMessages or frame.args[1] == "risk" or (count and count >= 100000) ) then local info = systemMessages and '.<br/>Changes to it can cause immediate changes to the Wikipedia user interface.' or '.' if infoArg then info = info .. "<br />" .. infoArg end sandbox_text = info .. '<br /> To avoid major disruption' .. (count and count >= 100000 and ' and server load' or '') .. ', any changes should be tested in the ' .. sandbox_text .. 'The tested changes can be added to this page in a single edit. ' else sandbox_text = (infoArg and ('.<br />' .. infoArg .. ' C') or ' and c') .. 'hanges may be widely noticed. Test changes in the ' .. sandbox_text end local discussion_text = systemMessages and 'Please discuss changes ' or 'Consider discussing changes ' if frame.args["2"] and frame.args["2"] ~= "" and frame.args["2"] ~= "yes" then discussion_text = string.format("%sat [[%s]]", discussion_text, frame.args["2"]) else discussion_text = string.format("%son the [[%s|talk page]]", discussion_text, title.talkPageTitle.fullText ) end return used_on_text .. sandbox_text .. discussion_text .. " before implementing them." .. bot_text end function p.main(frame) local count = nil if yesno(frame.args['fetch']) == false then if (frame.args[1] or '') ~= '' then count = tonumber(frame.args[1]) end else count = _fetch(frame) end local image = "[[File:Ambox warning yellow.svg|40px|alt=Warning|link=]]" local type_param = "style" local epilogue = '' if frame.args['system'] and frame.args['system'] ~= '' then image = "[[File:Ambox important.svg|40px|alt=Warning|link=]]" type_param = "content" local nocat = frame:getParent().args['nocat'] or frame.args['nocat'] local categorise = (nocat == '' or not yesno(nocat)) if categorise then epilogue = frame:preprocess('{{Sandbox other||{{#switch:{{#invoke:Effective protection level|{{#switch:{{NAMESPACE}}|File=upload|#default=edit}}|{{FULLPAGENAME}}}}|sysop|templateeditor|interfaceadmin=|#default=[[Category:Pages used in system messages needing protection]]}}}}') end elseif (frame.args[1] == "risk" or (count and count >= 100000)) then image = "[[File:Ambox warning orange.svg|40px|alt=Warning|link=]]" type_param = "content" end if frame.args["form"] == "editnotice" then return frame:expandTemplate{ title = 'editnotice', args = { ["image"] = image, ["text"] = p.text(frame, count), ["expiry"] = (frame.args["expiry"] or "") } } .. epilogue else return require('Module:Message box').main('ombox', { type = type_param, image = image, text = p.text(frame, count), expiry = (frame.args["expiry"] or "") }) .. epilogue end end return p 134551888e066954a89c109d2faa8af71a4454a4 Module:Clc 828 384 769 768 2023-07-10T15:48:07Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Clc]] wikitext text/x-wiki #REDIRECT [[Template:Category link with count]] 02280e2ab57b544236e11f913e3759c5781ca9d5 Module:Category link with count 828 385 771 770 2023-07-10T15:48:07Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Category_link_with_count]] wikitext text/x-wiki [[:Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|<!-- -->{{#if:{{{name|}}}|{{{name}}}|Category:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}}}<!-- -->]]&nbsp;({{PAGESINCATEGORY:{{#invoke:string|replace|1={{{1}}}|2=^:?[Cc]ategory:|3=|plain=false}}|{{{2|all}}}}})<noinclude> {{Documentation}} </noinclude> f93f1540b8c157703bd6d24ae35c35bef745981d Module:Transclusion count/data/I 828 386 773 772 2023-07-10T15:48:09Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Transclusion_count/data/I]] Scribunto text/plain return { ["IAAF_name"] = 2200, ["IAST"] = 6000, ["IBDB_name"] = 9100, ["ICD10"] = 4700, ["ICD9"] = 4400, ["ICS"] = 2900, ["IDN"] = 3300, ["IMDb_episode"] = 9900, ["IMDb_episodes"] = 2600, ["IMDb_name"] = 152000, ["IMDb_title"] = 187000, ["IMO_Number"] = 4100, ["IMSLP"] = 8200, ["INA"] = 2100, ["IND"] = 7400, ["INR"] = 6400, ["INRConvert"] = 5600, ["INRConvert/CurrentRate"] = 5500, ["INRConvert/USD"] = 5500, ["INRConvert/out"] = 5500, ["IOC_profile"] = 5300, ["IP"] = 2600, ["IPA"] = 141000, ["IPA-all"] = 3600, ["IPA-de"] = 8100, ["IPA-es"] = 8000, ["IPA-fr"] = 44000, ["IPA-it"] = 5900, ["IPA-nl"] = 3700, ["IPA-pl"] = 4000, ["IPA-pt"] = 3700, ["IPA-ru"] = 2700, ["IPA-sh"] = 2700, ["IPA-sl"] = 6900, ["IPA-th"] = 3000, ["IPA_audio_link"] = 19000, ["IPA_link"] = 3400, ["IPAc-cmn"] = 2600, ["IPAc-en"] = 48000, ["IPAc-pl"] = 52000, ["IPC_athlete"] = 2800, ["IPSummary"] = 78000, ["IP_summary"] = 78000, ["IPtalk"] = 22000, ["IPuser"] = 7000, ["IPvandal"] = 2700, ["IRC"] = 7300, ["IRI"] = 2200, ["IRL"] = 5400, ["IRN"] = 3500, ["ISBN"] = 461000, ["ISBNT"] = 38000, ["ISBN_missing"] = 2400, ["ISFDB_name"] = 4100, ["ISFDB_title"] = 4600, ["ISL"] = 2100, ["ISO_15924/script-example-character"] = 2800, ["ISO_15924/wp-article"] = 2800, ["ISO_15924/wp-article/format"] = 2800, ["ISO_15924/wp-article/label"] = 2700, ["ISO_3166_code"] = 511000, ["ISO_3166_name"] = 16000, ["ISO_639_name"] = 8000, ["ISP"] = 5300, ["ISR"] = 4700, ["ISSN"] = 12000, ["ISSN_link"] = 30000, ["ISTAT"] = 8100, ["ISU_figure_skater"] = 2500, ["ITA"] = 17000, ["ITF"] = 6100, ["ITF_profile"] = 9000, ["ITIS"] = 4400, ["ITN_talk"] = 9900, ["ITN_talk/date"] = 9900, ["IUCN_banner"] = 15000, ["I_sup"] = 4500, ["Iaaf_name"] = 7400, ["Ice_hockey"] = 19000, ["Ice_hockey_stats"] = 16000, ["Icehockeystats"] = 12000, ["Icon"] = 575000, ["If"] = 267000, ["If_all"] = 6300, ["If_between"] = 3700, ["If_both"] = 3040000, ["If_empty"] = 3620000, ["If_first_display_both"] = 73000, ["If_in_page"] = 9100, ["If_last_display_both"] = 30000, ["If_preview"] = 57000, ["If_then_show"] = 281000, ["Ifempty"] = 4200, ["Ifeq"] = 16000, ["Iferror_then_show"] = 3200, ["Ifexist_not_redirect"] = 1120000, ["Ifnotempty"] = 14000, ["Ifnumber"] = 35000, ["Ifsubst"] = 433000, ["Ih"] = 7500, ["Ill"] = 112000, ["Illm"] = 6800, ["Image_frame"] = 4900, ["Image_label"] = 4500, ["Image_label_begin"] = 3800, ["Image_label_end"] = 3500, ["Image_label_small"] = 2600, ["Image_needed"] = 4500, ["Image_other"] = 277000, ["Image_requested"] = 166000, ["Image_requested/Category_helper"] = 158000, ["Imbox"] = 915000, ["Imdb_name"] = 5300, ["Imdb_title"] = 4200, ["Import_style"] = 11000, ["Import_style/inputbox.css"] = 11000, ["Importance"] = 5580000, ["Importance/colour"] = 5590000, ["Importance_mask"] = 9620000, ["Improve_categories"] = 7300, ["Improve_documentation"] = 2200, ["In_class"] = 5700, ["In_lang"] = 353000, ["In_progress"] = 3200, ["In_string"] = 73000, ["In_title"] = 19000, ["Inactive_WikiProject_banner"] = 208000, ["Inactive_userpage_blanked"] = 4900, ["Include-USGov"] = 29000, ["Incomplete_list"] = 23000, ["Inconclusive"] = 2100, ["Increase"] = 42000, ["Incumbent_pope"] = 4300, ["Indent"] = 4200, ["IndexFungorum"] = 2200, ["Indian_English"] = 4300, ["Indian_Rupee"] = 10000, ["Indian_railway_code"] = 3100, ["Inflation"] = 19000, ["Inflation-fn"] = 5400, ["Inflation-year"] = 4400, ["Inflation/IN/startyear"] = 5500, ["Inflation/UK"] = 4300, ["Inflation/UK/dataset"] = 4300, ["Inflation/UK/startyear"] = 4300, ["Inflation/US"] = 12000, ["Inflation/US/dataset"] = 12000, ["Inflation/US/startyear"] = 12000, ["Inflation/fn"] = 6100, ["Inflation/year"] = 24000, ["Info"] = 7200, ["Infobox"] = 3200000, ["Infobox/Columns"] = 2400, ["Infobox/mobileviewfix.css"] = 142000, ["Infobox3cols"] = 16000, ["Infobox_AFL_biography"] = 14000, ["Infobox_Aircraft_Begin"] = 5400, ["Infobox_Aircraft_Type"] = 4700, ["Infobox_Athletics_Championships"] = 2700, ["Infobox_Australian_place"] = 15000, ["Infobox_CFL_biography"] = 2200, ["Infobox_COA_wide"] = 3100, ["Infobox_Canada_electoral_district"] = 2400, ["Infobox_Canadian_Football_League_biography"] = 5800, ["Infobox_Canadian_Football_League_biography/position"] = 5700, ["Infobox_Chinese"] = 20000, ["Infobox_Chinese/Chinese"] = 2700, ["Infobox_Chinese/Footer"] = 8700, ["Infobox_Chinese/Header"] = 8700, ["Infobox_Chinese/Korean"] = 17000, ["Infobox_Christian_leader"] = 18000, ["Infobox_Election"] = 2200, ["Infobox_French_commune"] = 38000, ["Infobox_GAA_player"] = 2800, ["Infobox_Gaelic_games_player"] = 5000, ["Infobox_German_location"] = 13000, ["Infobox_German_place"] = 14000, ["Infobox_Grand_Prix_race_report"] = 2000, ["Infobox_Greece_place"] = 2800, ["Infobox_Greek_Dimos"] = 2800, ["Infobox_Hindu_temple"] = 2400, ["Infobox_Indian_constituency"] = 4600, ["Infobox_Indian_constituency/defaultdata"] = 4600, ["Infobox_Italian_comune"] = 8100, ["Infobox_Korean_name"] = 15000, ["Infobox_Korean_name/categories"] = 15000, ["Infobox_MLB_yearly"] = 3100, ["Infobox_NASCAR_race_report"] = 2100, ["Infobox_NCAA_team_season"] = 18000, ["Infobox_NFL_biography"] = 28000, ["Infobox_NFL_player"] = 7900, ["Infobox_NFL_season"] = 2500, ["Infobox_NFL_team_season"] = 3900, ["Infobox_NRHP"] = 72000, ["Infobox_NRHP/conv"] = 18000, ["Infobox_NRHP/locmapin2region"] = 66000, ["Infobox_Officeholder"] = 4900, ["Infobox_Olympic_event"] = 7300, ["Infobox_Olympic_event/games_text"] = 7300, ["Infobox_Paralympic_event"] = 2600, ["Infobox_Paralympic_event/games_text"] = 2600, ["Infobox_Politician"] = 2200, ["Infobox_Romanian_subdivision"] = 3100, ["Infobox_Russian_district"] = 2000, ["Infobox_Russian_inhabited_locality"] = 4400, ["Infobox_SCOTUS_case"] = 3700, ["Infobox_Site_of_Special_Scientific_Interest"] = 2000, ["Infobox_Swiss_town"] = 2800, ["Infobox_Switzerland_municipality"] = 2900, ["Infobox_Turkey_place"] = 14000, ["Infobox_U.S._county"] = 3000, ["Infobox_U.S._county/district"] = 3000, ["Infobox_UK_constituency"] = 2100, ["Infobox_UK_constituency/year"] = 2100, ["Infobox_UK_legislation"] = 3100, ["Infobox_UK_place"] = 26000, ["Infobox_UK_place/NoDialCode"] = 7900, ["Infobox_UK_place/NoPostCode"] = 3000, ["Infobox_UK_place/area"] = 2400, ["Infobox_UK_place/dist"] = 2500, ["Infobox_UK_place/local"] = 26000, ["Infobox_UK_place/styles.css"] = 26000, ["Infobox_UN_resolution"] = 2300, ["Infobox_US_Supreme_Court_case"] = 3800, ["Infobox_US_Supreme_Court_case/courts"] = 3800, ["Infobox_Wikipedia_user"] = 9600, ["Infobox_YouTube_personality"] = 2600, ["Infobox_YouTube_personality/styles.css"] = 2600, ["Infobox_academic"] = 13000, ["Infobox_aircraft_begin"] = 14000, ["Infobox_aircraft_occurrence"] = 2300, ["Infobox_aircraft_type"] = 12000, ["Infobox_airline"] = 4600, ["Infobox_airport"] = 15000, ["Infobox_airport/datatable"] = 15000, ["Infobox_album"] = 161000, ["Infobox_album/color"] = 190000, ["Infobox_album/link"] = 161000, ["Infobox_anatomy"] = 4400, ["Infobox_ancient_site"] = 5300, ["Infobox_animanga/Footer"] = 6700, ["Infobox_animanga/Header"] = 6700, ["Infobox_animanga/Print"] = 5400, ["Infobox_animanga/Video"] = 4700, ["Infobox_architect"] = 3700, ["Infobox_artist"] = 28000, ["Infobox_artist_discography"] = 5900, ["Infobox_artwork"] = 11000, ["Infobox_athlete"] = 3000, ["Infobox_automobile"] = 8400, ["Infobox_award"] = 13000, ["Infobox_badminton_player"] = 3200, ["Infobox_baseball_biography"] = 28000, ["Infobox_baseball_biography/style"] = 28000, ["Infobox_baseball_biography/styles.css"] = 28000, ["Infobox_basketball_biography"] = 21000, ["Infobox_basketball_biography/style"] = 21000, ["Infobox_basketball_club"] = 3000, ["Infobox_beauty_pageant"] = 2400, ["Infobox_bilateral_relations"] = 4300, ["Infobox_body_of_water"] = 18000, ["Infobox_book"] = 52000, ["Infobox_boxer"] = 5700, ["Infobox_bridge"] = 6000, ["Infobox_building"] = 27000, ["Infobox_character"] = 7600, ["Infobox_chess_biography"] = 3700, ["Infobox_chess_player"] = 3100, ["Infobox_church"] = 15000, ["Infobox_church/denomination"] = 15000, ["Infobox_church/font_color"] = 15000, ["Infobox_civil_conflict"] = 2300, ["Infobox_civilian_attack"] = 5400, ["Infobox_college_coach"] = 11000, ["Infobox_college_football_game"] = 2100, ["Infobox_college_sports_team_season"] = 39000, ["Infobox_college_sports_team_season/link"] = 39000, ["Infobox_college_sports_team_season/name"] = 39000, ["Infobox_college_sports_team_season/succession"] = 39000, ["Infobox_college_sports_team_season/team"] = 39000, ["Infobox_comic_book_title"] = 2900, ["Infobox_comics_character"] = 3600, ["Infobox_comics_creator"] = 3500, ["Infobox_comics_creator/styles.css"] = 3500, ["Infobox_company"] = 83000, ["Infobox_computing_device"] = 2300, ["Infobox_concert"] = 3200, ["Infobox_constituency"] = 5100, ["Infobox_country"] = 6300, ["Infobox_country/formernext"] = 6000, ["Infobox_country/imagetable"] = 5200, ["Infobox_country/multirow"] = 8200, ["Infobox_country/status_text"] = 2700, ["Infobox_country/styles.css"] = 6300, ["Infobox_country_at_games"] = 15000, ["Infobox_country_at_games/core"] = 15000, ["Infobox_country_at_games/see_also"] = 12000, ["Infobox_court_case"] = 4600, ["Infobox_court_case/images"] = 2400, ["Infobox_cricket_tournament"] = 2300, ["Infobox_cricketer"] = 32000, ["Infobox_cricketer/career"] = 32000, ["Infobox_cricketer/national_side"] = 7500, ["Infobox_criminal"] = 6300, ["Infobox_curler"] = 2600, ["Infobox_cycling_race_report"] = 4500, ["Infobox_cyclist"] = 16000, ["Infobox_dam"] = 5600, ["Infobox_designation_list"] = 19000, ["Infobox_designation_list/entry"] = 17000, ["Infobox_dim"] = 6900, ["Infobox_dim/core"] = 6900, ["Infobox_diocese"] = 3800, ["Infobox_drug"] = 9500, ["Infobox_drug/chemical_formula"] = 9500, ["Infobox_drug/data_page_link"] = 9500, ["Infobox_drug/formatATC"] = 9400, ["Infobox_drug/formatCASnumber"] = 9500, ["Infobox_drug/formatChEBI"] = 9500, ["Infobox_drug/formatChEMBL"] = 9500, ["Infobox_drug/formatChemDBNIAID"] = 9500, ["Infobox_drug/formatChemSpider"] = 9500, ["Infobox_drug/formatCompTox"] = 9500, ["Infobox_drug/formatDrugBank"] = 9500, ["Infobox_drug/formatIUPHARBPS"] = 9500, ["Infobox_drug/formatJmol"] = 9500, ["Infobox_drug/formatKEGG"] = 9500, ["Infobox_drug/formatPDBligand"] = 8800, ["Infobox_drug/formatPubChemCID"] = 9500, ["Infobox_drug/formatPubChemSID"] = 9500, ["Infobox_drug/formatUNII"] = 9500, ["Infobox_drug/legal_status"] = 9600, ["Infobox_drug/licence"] = 9600, ["Infobox_drug/maintenance_categories"] = 9500, ["Infobox_drug/non-ref-space"] = 3900, ["Infobox_drug/pregnancy_category"] = 9600, ["Infobox_drug/title"] = 9500, ["Infobox_election"] = 29000, ["Infobox_election/row"] = 29000, ["Infobox_election/shortname"] = 28000, ["Infobox_enzyme"] = 5100, ["Infobox_ethnic_group"] = 7200, ["Infobox_event"] = 5300, ["Infobox_family"] = 2100, ["Infobox_figure_skater"] = 4200, ["Infobox_film"] = 155000, ["Infobox_film/short_description"] = 151000, ["Infobox_film_awards"] = 2600, ["Infobox_film_awards/link"] = 2600, ["Infobox_film_awards/style"] = 2600, ["Infobox_food"] = 6800, ["Infobox_football_biography"] = 205000, ["Infobox_football_club"] = 27000, ["Infobox_football_club_season"] = 20000, ["Infobox_football_league"] = 2500, ["Infobox_football_league_season"] = 19000, ["Infobox_football_match"] = 5800, ["Infobox_football_tournament_season"] = 8000, ["Infobox_former_subdivision"] = 3300, ["Infobox_former_subdivision/styles.css"] = 3300, ["Infobox_galaxy"] = 2100, ["Infobox_game"] = 2500, ["Infobox_game_score"] = 3400, ["Infobox_gene"] = 13000, ["Infobox_given_name"] = 4000, ["Infobox_golfer"] = 4400, ["Infobox_golfer/highest_ranking"] = 4400, ["Infobox_government_agency"] = 10000, ["Infobox_government_cabinet"] = 2500, ["Infobox_gridiron_football_person"] = 2500, ["Infobox_gridiron_football_person/position"] = 5700, ["Infobox_gymnast"] = 3400, ["Infobox_handball_biography"] = 4900, ["Infobox_historic_site"] = 11000, ["Infobox_horseraces"] = 2600, ["Infobox_hospital"] = 6300, ["Infobox_hospital/care_system"] = 6300, ["Infobox_hospital/lists"] = 6300, ["Infobox_ice_hockey_biography"] = 20000, ["Infobox_ice_hockey_player"] = 19000, ["Infobox_ice_hockey_team"] = 3000, ["Infobox_ice_hockey_team_season"] = 2000, ["Infobox_international_football_competition"] = 5700, ["Infobox_islands"] = 8700, ["Infobox_islands/area"] = 9100, ["Infobox_islands/density"] = 9100, ["Infobox_islands/length"] = 8700, ["Infobox_islands/styles.css"] = 8700, ["Infobox_journal"] = 9700, ["Infobox_journal/Abbreviation_search"] = 9600, ["Infobox_journal/Bluebook_check"] = 9400, ["Infobox_journal/Former_check"] = 9400, ["Infobox_journal/ISO_4_check"] = 9400, ["Infobox_journal/ISSN-eISSN"] = 9400, ["Infobox_journal/Indexing_search"] = 9500, ["Infobox_journal/MathSciNet_check"] = 9400, ["Infobox_journal/NLM_check"] = 9400, ["Infobox_journal/frequency"] = 8600, ["Infobox_lake"] = 4400, ["Infobox_language"] = 9500, ["Infobox_language/family-color"] = 11000, ["Infobox_language/genetic"] = 6500, ["Infobox_language/linguistlist"] = 9500, ["Infobox_language/ref"] = 7100, ["Infobox_legislature"] = 3600, ["Infobox_library"] = 2100, ["Infobox_lighthouse"] = 2600, ["Infobox_lighthouse/light"] = 2600, ["Infobox_locomotive"] = 4900, ["Infobox_magazine"] = 7600, ["Infobox_manner_of_address"] = 3300, ["Infobox_mapframe"] = 79000, ["Infobox_martial_artist"] = 5700, ["Infobox_martial_artist/record"] = 5700, ["Infobox_medal_templates"] = 420000, ["Infobox_medical_condition"] = 10000, ["Infobox_medical_condition_(new)"] = 8200, ["Infobox_medical_details"] = 2000, ["Infobox_military_conflict"] = 22000, ["Infobox_military_installation"] = 9700, ["Infobox_military_person"] = 44000, ["Infobox_military_unit"] = 26000, ["Infobox_mine"] = 2100, ["Infobox_model"] = 2300, ["Infobox_mountain"] = 28000, ["Infobox_multi-sport_competition_event"] = 2300, ["Infobox_museum"] = 10000, ["Infobox_musical_artist"] = 121000, ["Infobox_musical_artist/color"] = 121000, ["Infobox_musical_artist/hCard_class"] = 311000, ["Infobox_musical_composition"] = 2800, ["Infobox_name"] = 7400, ["Infobox_name_module"] = 6700, ["Infobox_newspaper"] = 9600, ["Infobox_nobility"] = 2400, ["Infobox_noble"] = 7200, ["Infobox_officeholder"] = 214000, ["Infobox_officeholder/office"] = 220000, ["Infobox_official_post"] = 7900, ["Infobox_organization"] = 36000, ["Infobox_pageant_titleholder"] = 2800, ["Infobox_park"] = 7300, ["Infobox_person"] = 467000, ["Infobox_person/Wikidata"] = 4900, ["Infobox_person/height"] = 101000, ["Infobox_person/length"] = 7000, ["Infobox_person/weight"] = 66000, ["Infobox_philosopher"] = 3300, ["Infobox_planet"] = 4700, ["Infobox_play"] = 3800, ["Infobox_political_party"] = 14000, ["Infobox_power_station"] = 3000, ["Infobox_prepared_food"] = 3100, ["Infobox_professional_wrestler"] = 4200, ["Infobox_professional_wrestling_event"] = 2600, ["Infobox_protected_area"] = 14000, ["Infobox_protein_family"] = 2100, ["Infobox_publisher"] = 2400, ["Infobox_racehorse"] = 5500, ["Infobox_racing_driver"] = 3800, ["Infobox_radio_station"] = 22000, ["Infobox_rail"] = 2900, ["Infobox_rail_line"] = 7200, ["Infobox_rail_line/tracking"] = 7200, ["Infobox_rail_service"] = 2900, ["Infobox_rail_service/doc"] = 2900, ["Infobox_reality_competition_season"] = 3400, ["Infobox_record_label"] = 4000, ["Infobox_recurring_event"] = 6300, ["Infobox_religious_biography"] = 5100, ["Infobox_religious_building"] = 12000, ["Infobox_religious_building/color"] = 17000, ["Infobox_restaurant"] = 2500, ["Infobox_river"] = 30000, ["Infobox_river/calcunit"] = 30000, ["Infobox_river/discharge"] = 30000, ["Infobox_river/row-style"] = 30000, ["Infobox_river/source"] = 30000, ["Infobox_road"] = 24000, ["Infobox_road/meta/mask/category"] = 23000, ["Infobox_road/meta/mask/country"] = 24000, ["Infobox_road/styles.css"] = 25000, ["Infobox_road_small"] = 2300, ["Infobox_rockunit"] = 6400, ["Infobox_royalty"] = 21000, ["Infobox_royalty/short_description"] = 13000, ["Infobox_rugby_biography"] = 16000, ["Infobox_rugby_biography/correct_date"] = 16000, ["Infobox_rugby_biography/depcheck"] = 15000, ["Infobox_rugby_league_biography"] = 9900, ["Infobox_rugby_league_biography/PLAYER"] = 9800, ["Infobox_rugby_team"] = 2600, ["Infobox_sailboat_specifications"] = 2300, ["Infobox_saint"] = 4900, ["Infobox_school"] = 38000, ["Infobox_school/short_description"] = 38000, ["Infobox_school_district"] = 5600, ["Infobox_school_district/styles.css"] = 5600, ["Infobox_scientist"] = 48000, ["Infobox_service_record"] = 2600, ["Infobox_settlement"] = 559000, ["Infobox_settlement/areadisp"] = 233000, ["Infobox_settlement/columns"] = 94000, ["Infobox_settlement/columns/styles.css"] = 94000, ["Infobox_settlement/densdisp"] = 432000, ["Infobox_settlement/impus"] = 81000, ["Infobox_settlement/lengthdisp"] = 168000, ["Infobox_settlement/link"] = 93000, ["Infobox_settlement/metric"] = 207000, ["Infobox_settlement/pref"] = 288000, ["Infobox_settlement/styles.css"] = 559000, ["Infobox_ship_begin"] = 41000, ["Infobox_ship_career"] = 37000, ["Infobox_ship_characteristics"] = 40000, ["Infobox_ship_class_overview"] = 4100, ["Infobox_ship_image"] = 40000, ["Infobox_shopping_mall"] = 3400, ["Infobox_short_story"] = 2300, ["Infobox_skier"] = 2500, ["Infobox_soap_character"] = 2900, ["Infobox_software"] = 14000, ["Infobox_software/simple"] = 14000, ["Infobox_song"] = 75000, ["Infobox_song/color"] = 75000, ["Infobox_song/link"] = 75000, ["Infobox_spaceflight"] = 3500, ["Infobox_spaceflight/styles.css"] = 3500, ["Infobox_sport_event"] = 2100, ["Infobox_sports_competition_event"] = 16000, ["Infobox_sports_competition_event/medalrow"] = 11000, ["Infobox_sports_league"] = 5000, ["Infobox_sports_season"] = 5300, ["Infobox_sports_team"] = 2200, ["Infobox_sportsperson"] = 106000, ["Infobox_stadium"] = 3500, ["Infobox_station"] = 55000, ["Infobox_station/doc"] = 55000, ["Infobox_station/services"] = 55000, ["Infobox_station/styles.css"] = 55000, ["Infobox_street"] = 3400, ["Infobox_swimmer"] = 9300, ["Infobox_television"] = 56000, ["Infobox_television/Short_description"] = 54000, ["Infobox_television_channel"] = 6200, ["Infobox_television_episode"] = 12000, ["Infobox_television_episode/styles.css"] = 12000, ["Infobox_television_season"] = 9300, ["Infobox_television_station"] = 3700, ["Infobox_tennis_biography"] = 10000, ["Infobox_tennis_event"] = 2400, ["Infobox_tennis_tournament_event"] = 18000, ["Infobox_tennis_tournament_year"] = 9100, ["Infobox_tennis_tournament_year/color"] = 28000, ["Infobox_tennis_tournament_year/footer"] = 28000, ["Infobox_train"] = 2300, ["Infobox_union"] = 2100, ["Infobox_university"] = 26000, ["Infobox_user"] = 2600, ["Infobox_venue"] = 18000, ["Infobox_video_game"] = 28000, ["Infobox_video_game/styles.css"] = 28000, ["Infobox_volleyball_biography"] = 5200, ["Infobox_weapon"] = 7300, ["Infobox_website"] = 7700, ["Infobox_writer"] = 38000, ["Information"] = 103000, ["Information/styles.css"] = 103000, ["Inprogress"] = 2300, ["Input_link"] = 32000, ["Instagram"] = 11000, ["Interlanguage_link"] = 149000, ["Interlanguage_link_multi"] = 19000, ["Internet_Archive_author"] = 18000, ["Internet_Archive_film"] = 2500, ["Intitle"] = 12000, ["Invalid_SVG"] = 3800, ["Invalid_SVG/styles.css"] = 3800, ["Iptalk"] = 21000, ["IranCensus2006"] = 49000, ["IranNCSGN"] = 3200, ["Iran_Census_2006"] = 49000, ["Irc"] = 2100, ["Irish_place_name"] = 2600, ["IsIPAddress"] = 40000, ["IsValidPageName"] = 139000, ["Is_country_in_Central_America"] = 13000, ["Is_country_in_the_Caribbean"] = 14000, ["Is_interwiki_link"] = 6100, ["Is_italic_taxon"] = 472000, ["Is_redirect"] = 26000, ["Isbn"] = 7300, ["Isfdb_name"] = 3800, ["Isfdb_title"] = 4500, ["Isnumeric"] = 204000, ["Iso2continent"] = 35000, ["Iso2country"] = 23000, ["Iso2country/article"] = 22000, ["Iso2country/data"] = 23000, ["Iso2nationality"] = 222000, ["Issubst"] = 71000, ["Isu_name"] = 2200, ["Italic_dab2"] = 5200, ["Italic_title"] = 283000, ["Italic_title_prefixed"] = 8600, ["Italics_colon"] = 3700, ["Italictitle"] = 4300, ["Ivm"] = 5700, ["Ivm/styles.css"] = 5700, ["Ivmbox"] = 122000, ["Ivory_messagebox"] = 139000, ["Module:I18n/complex_date"] = 65000, ["Module:IP"] = 129000, ["Module:IPA_symbol"] = 4700, ["Module:IPA_symbol/data"] = 4700, ["Module:IPAc-en"] = 48000, ["Module:IPAc-en/data"] = 48000, ["Module:IPAc-en/phonemes"] = 48000, ["Module:IPAc-en/pronunciation"] = 48000, ["Module:IPAddress"] = 189000, ["Module:ISO_3166"] = 1020000, ["Module:ISO_3166/data/AT"] = 2500, ["Module:ISO_3166/data/BA"] = 3400, ["Module:ISO_3166/data/CA"] = 2500, ["Module:ISO_3166/data/CN"] = 2100, ["Module:ISO_3166/data/DE"] = 14000, ["Module:ISO_3166/data/ES"] = 3600, ["Module:ISO_3166/data/FR"] = 38000, ["Module:ISO_3166/data/GB"] = 6400, ["Module:ISO_3166/data/GR"] = 3100, ["Module:ISO_3166/data/IN"] = 28000, ["Module:ISO_3166/data/IR"] = 5600, ["Module:ISO_3166/data/National"] = 1020000, ["Module:ISO_3166/data/PL"] = 5800, ["Module:ISO_3166/data/RS"] = 3200, ["Module:ISO_3166/data/RU"] = 24000, ["Module:ISO_3166/data/US"] = 85000, ["Module:ISO_639_name"] = 20000, ["Module:ISOdate"] = 65000, ["Module:Icon"] = 575000, ["Module:Icon/data"] = 575000, ["Module:If_empty"] = 3620000, ["Module:If_in_page"] = 9100, ["Module:If_preview"] = 544000, ["Module:If_preview/configuration"] = 544000, ["Module:If_preview/styles.css"] = 544000, ["Module:Import_style"] = 11000, ["Module:In_lang"] = 353000, ["Module:Indent"] = 4200, ["Module:Infobox"] = 4070000, ["Module:Infobox/dates"] = 66000, ["Module:Infobox/styles.css"] = 4320000, ["Module:Infobox3cols"] = 295000, ["Module:InfoboxImage"] = 4370000, ["Module:Infobox_body_of_water_tracking"] = 18000, ["Module:Infobox_cyclist_tracking"] = 16000, ["Module:Infobox_gene"] = 13000, ["Module:Infobox_mapframe"] = 399000, ["Module:Infobox_military_conflict"] = 22000, ["Module:Infobox_military_conflict/styles.css"] = 22000, ["Module:Infobox_multi-lingual_name"] = 20000, ["Module:Infobox_multi-lingual_name/data"] = 20000, ["Module:Infobox_power_station"] = 3000, ["Module:Infobox_road"] = 25000, ["Module:Infobox_road/browselinks"] = 25000, ["Module:Infobox_road/errors"] = 24000, ["Module:Infobox_road/length"] = 25000, ["Module:Infobox_road/locations"] = 24000, ["Module:Infobox_road/map"] = 24000, ["Module:Infobox_road/route"] = 25000, ["Module:Infobox_road/sections"] = 24000, ["Module:Infobox_television"] = 56000, ["Module:Infobox_television_disambiguation_check"] = 63000, ["Module:Infobox_television_episode"] = 12000, ["Module:Infobox_television_season_disambiguation_check"] = 8900, ["Module:Infobox_television_season_name"] = 9300, ["Module:Internet_Archive"] = 18000, ["Module:IrelandByCountyCatNav"] = 3400, ["Module:Is_infobox_in_lead"] = 376000, ["Module:Is_instance"] = 333000, ["Module:Italic_title"] = 1110000, ["Module:Italic_title2"] = 5200, } 0b605ade3d48ca6718793fd9fac6abbdb09b1f51 Module:Infobox/doc 828 387 775 774 2023-07-10T15:48:10Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Infobox/doc]] wikitext text/x-wiki {{High-use|3308957|all-pages = yes}} {{module rating|protected}} {{Lua|Module:Navbar|Module:Italic title}} {{Uses TemplateStyles|Module:Infobox/styles.css|Template:Hlist/styles.css|Template:Plainlist/styles.css}} '''Module:Infobox''' is a [[WP:Module|module]] that implements the {{tl|Infobox}} template. Please see the template page for usage instructions. == Tracking categories == * {{clc|Pages using infobox templates with ignored data cells}} * {{clc|Articles using infobox templates with no data rows}} * {{clc|Pages using embedded infobox templates with the title parameter}} <includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox|| [[Category:Modules that add a tracking category]] [[Category:Wikipedia infoboxes]] [[Category:Infobox modules]] [[Category:Modules that check for strip markers]] }}</includeonly> 936ad219eb263a6f3293d62f667bd7b5db1059c1 Module:Plainlist/styles.css 828 388 777 776 2023-07-10T15:48:10Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Plainlist/styles.css]] text text/plain /* {{pp-template|small=yes}} */ .plainlist ol, .plainlist ul { line-height: inherit; list-style: none; margin: 0; padding: 0; /* Reset Minerva default */ } .plainlist ol li, .plainlist ul li { margin-bottom: 0; } 51706efa229ff8794c0d94f260a208e7c5e6ec30 Module:Hlist/styles.css 828 389 779 778 2023-07-10T15:48:10Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Hlist/styles.css]] text text/plain /* {{pp-protected|reason=match parent|small=yes}} */ /* * hlist styles are defined in core and Minerva and differ in Minerva. The * current definitions here (2023-01-01) are sufficient to override Minerva * without use of the hlist-separated class. The most problematic styles were * related to margin, padding, and the bullet. Check files listed at * [[MediaWiki talk:Common.css/to do#hlist-separated]] */ /* * TODO: When the majority of readership supports it (or some beautiful world * in which grade C support is above the minimum threshold), use :is() */ .hlist dl, .hlist ol, .hlist ul { margin: 0; padding: 0; } /* Display list items inline */ .hlist dd, .hlist dt, .hlist li { /* * don't trust the note that says margin doesn't work with inline * removing margin: 0 makes dds have margins again * We also want to reset margin-right in Minerva */ margin: 0; display: inline; } /* Display requested top-level lists inline */ .hlist.inline, .hlist.inline dl, .hlist.inline ol, .hlist.inline ul, /* Display nested lists inline */ .hlist dl dl, .hlist dl ol, .hlist dl ul, .hlist ol dl, .hlist ol ol, .hlist ol ul, .hlist ul dl, .hlist ul ol, .hlist ul ul { display: inline; } /* Hide empty list items */ .hlist .mw-empty-li { display: none; } /* TODO: :not() can maybe be used here to remove the later rule. naive test * seems to work. more testing needed. like so: *.hlist dt:not(:last-child)::after { * content: ": "; *} *.hlist dd:not(:last-child)::after, *.hlist li:not(:last-child)::after { * content: " · "; * font-weight: bold; *} */ /* Generate interpuncts */ .hlist dt::after { content: ": "; } .hlist dd::after, .hlist li::after { content: " · "; font-weight: bold; } .hlist dd:last-child::after, .hlist dt:last-child::after, .hlist li:last-child::after { content: none; } /* Add parentheses around nested lists */ .hlist dd dd:first-child::before, .hlist dd dt:first-child::before, .hlist dd li:first-child::before, .hlist dt dd:first-child::before, .hlist dt dt:first-child::before, .hlist dt li:first-child::before, .hlist li dd:first-child::before, .hlist li dt:first-child::before, .hlist li li:first-child::before { content: " ("; font-weight: normal; } .hlist dd dd:last-child::after, .hlist dd dt:last-child::after, .hlist dd li:last-child::after, .hlist dt dd:last-child::after, .hlist dt dt:last-child::after, .hlist dt li:last-child::after, .hlist li dd:last-child::after, .hlist li dt:last-child::after, .hlist li li:last-child::after { content: ")"; font-weight: normal; } /* Put ordinals in front of ordered list items */ .hlist ol { counter-reset: listitem; } .hlist ol > li { counter-increment: listitem; } .hlist ol > li::before { content: " " counter(listitem) "\a0"; } .hlist dd ol > li:first-child::before, .hlist dt ol > li:first-child::before, .hlist li ol > li:first-child::before { content: " (" counter(listitem) "\a0"; } 8c9dd9c9c00f30eead17fe10f51d183333e81f33 Module:Uses TemplateStyles/doc 828 390 781 780 2023-07-10T15:49:47Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Uses_TemplateStyles/doc]] wikitext text/x-wiki {{Lua|Module:Uses TemplateStyles/config|Module:Yesno|Module:List|Module:TableTools|Module:Message box|Module:TNT}} <!-- uses data [[c:Data:I18n/Uses_TemplateStyles.tab]] --> Implements {{tl|Uses TemplateStyles}} 9374abbfb0d2232c7218828ac2d9727c43e6e04c Template:Multiple issues 10 392 785 784 2023-07-10T15:57:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Multiple_issues]] wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||$B= {{Ambox |name = Multiple issues |templatestyles = Multiple issues/styles.css |doc = no |subst = <includeonly>{{subst:substcheck}}</includeonly> |class = ambox-multiple_issues compact-ambox |type = content |removalnotice = yes <!-- as of 2016-06, param does not work on this template --> |cat=Articles with multiple maintenance issues |text = <div class="multiple-issues-text {{#if:{{{1|}}}|mw-collapsible {{#ifeq:{{{collapsed}}}|yes|mw-collapsed}}}}"><!-- -->'''This {{#if:{{{section|}}}|section|article}} has multiple issues.''' Please help '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} improve it]''' or discuss these issues on the '''[[{{TALKPAGENAME}}|talk page]]'''. <small>''([[Help:Maintenance template removal|Learn how and when to remove these template messages]])''</small> {{#if:{{{1|}}} | <div class="mw-collapsible-content"> {{#invoke:String|replace|source={{{1|}}}|pattern=style="display: none"|replace=|count=}}<!--remove style="display: none", to support display of {{orphan}} messages in {{multiple issues}}--> </div> | <includeonly>{{error|No issues specified. Please specify issues, or remove this template.}}</includeonly> }} </div> {{#if:{{{2|}}}|[[Category:Pages using multiple issues with unknown parameters|§{{PAGENAME}}]]}} }}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using multiple issues with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Multiple issues]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | 2 | collapsed | section }} }}<noinclude> {{Documentation}} </noinclude> 901c03957b27f754d3caf69d5c74740f793c9a03 Template:Multiple issues/styles.css 10 393 787 786 2023-07-10T15:57:43Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Multiple_issues/styles.css]] text text/plain /* {{pp|small=y}} */ .multiple-issues-text { width: 95%; margin: 0.2em 0; } .multiple-issues-text > .mw-collapsible-content { margin-top: 0.3em; } /* Remove borders, backgrounds, padding, etc. */ .compact-ambox .ambox { border: none; border-collapse: collapse; background-color: transparent; margin: 0 0 0 1.6em !important; padding: 0 !important; width: auto; display: block; } body.mediawiki .compact-ambox .ambox.mbox-small-left { font-size: 100%; width: auto; margin: 0; } /* Style the text cell as a list item and remove its padding */ .compact-ambox .ambox .mbox-text { padding: 0 !important; margin: 0 !important; } .compact-ambox .ambox .mbox-text-span { display: list-item; line-height: 1.5em; list-style-type: disc; } /* Hide the images */ .compact-ambox .ambox .mbox-image, .compact-ambox .ambox .mbox-imageright, .compact-ambox .ambox .mbox-empty-cell, /* Allow for hiding text in compact form */ .compact-ambox .hide-when-compact { display: none; } e90883916010fd38cd4f9c7e10c4a01908c965cb Template:Ambox 10 394 789 788 2023-07-10T15:57:44Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Ambox]] wikitext text/x-wiki {{#invoke:Message box|ambox}}{{#ifeq:{{{small}}};{{NAMESPACENUMBER}}|left;0|[[Category:Articles using small message boxes]]}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 0c13ec156138ae0499c998cc3d7fbbeac4aeeed6 Module:Unsubst 828 395 791 790 2023-07-10T15:57:44Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Unsubst]] Scribunto text/plain local checkType = require('libraryUtil').checkType local p = {} local BODY_PARAM = '$B' local specialParams = { ['$params'] = 'parameter list', ['$aliases'] = 'parameter aliases', ['$flags'] = 'flags', ['$B'] = 'template content', ['$template-name'] = 'template invocation name override', } function p.main(frame, body) -- If we are substing, this function returns a template invocation, and if -- not, it returns the template body. The template body can be specified in -- the body parameter, or in the template parameter defined in the -- BODY_PARAM variable. This function can be called from Lua or from -- #invoke. -- Return the template body if we aren't substing. if not mw.isSubsting() then if body ~= nil then return body elseif frame.args[BODY_PARAM] ~= nil then return frame.args[BODY_PARAM] else error(string.format( "no template content specified (use parameter '%s' from #invoke)", BODY_PARAM ), 2) end end -- Sanity check for the frame object. if type(frame) ~= 'table' or type(frame.getParent) ~= 'function' or not frame:getParent() then error( "argument #1 to 'main' must be a frame object with a parent " .. "frame available", 2 ) end -- Find the invocation name. local mTemplateInvocation = require('Module:Template invocation') local name if frame.args['$template-name'] and '' ~= frame.args['$template-name'] then name = frame.args['$template-name'] -- override whatever the template name is with this name else name = mTemplateInvocation.name(frame:getParent():getTitle()) end -- Combine passed args with passed defaults local args = {} if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*override%s*,' ) then for k, v in pairs( frame:getParent().args ) do args[k] = v end for k, v in pairs( frame.args ) do if not specialParams[k] then if v == '__DATE__' then v = mw.getContentLanguage():formatDate( 'F Y' ) end args[k] = v end end else for k, v in pairs( frame.args ) do if not specialParams[k] then if v == '__DATE__' then v = mw.getContentLanguage():formatDate( 'F Y' ) end args[k] = v end end for k, v in pairs( frame:getParent().args ) do args[k] = v end end -- Trim parameters, if not specified otherwise if not string.find( ','..(frame.args['$flags'] or '')..',', ',%s*keep%-whitespace%s*,' ) then for k, v in pairs( args ) do args[k] = mw.ustring.match(v, '^%s*(.*)%s*$') or '' end end -- Pull information from parameter aliases local aliases = {} if frame.args['$aliases'] then local list = mw.text.split( frame.args['$aliases'], '%s*,%s*' ) for k, v in ipairs( list ) do local tmp = mw.text.split( v, '%s*>%s*' ) aliases[tonumber(mw.ustring.match(tmp[1], '^[1-9][0-9]*$')) or tmp[1]] = ((tonumber(mw.ustring.match(tmp[2], '^[1-9][0-9]*$'))) or tmp[2]) end end for k, v in pairs( aliases ) do if args[k] and ( not args[v] or args[v] == '' ) then args[v] = args[k] end args[k] = nil end -- Remove empty parameters, if specified if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*remove%-empty%s*,' ) then local tmp = 0 for k, v in ipairs( args ) do if v ~= '' or ( args[k+1] and args[k+1] ~= '' ) or ( args[k+2] and args[k+2] ~= '' ) then tmp = k else break end end for k, v in pairs( args ) do if v == '' then if not (type(k) == 'number' and k < tmp) then args[k] = nil end end end end -- Order parameters if frame.args['$params'] then local params, tmp = mw.text.split( frame.args['$params'], '%s*,%s*' ), {} for k, v in ipairs(params) do v = tonumber(mw.ustring.match(v, '^[1-9][0-9]*$')) or v if args[v] then tmp[v], args[v] = args[v], nil end end for k, v in pairs(args) do tmp[k], args[k] = args[k], nil end args = tmp end return mTemplateInvocation.invocation(name, args) end p[''] = p.main -- For backwards compatibility return p 7f01ffc8aa2ac4a4772f14c12e0b77e384ecabb6 Module:Citation/CS1 828 396 793 792 2023-07-10T15:57:45Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1]] Scribunto text/plain require('strict'); --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- each of these counts against the Lua upvalue limit ]] local validation; -- functions in Module:Citation/CS1/Date_validation local utilities; -- functions in Module:Citation/CS1/Utilities local z = {}; -- table of tables in Module:Citation/CS1/Utilities local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers local metadata; -- functions in Module:Citation/CS1/COinS local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist --[[------------------< P A G E S C O P E V A R I A B L E S >--------------- declare variables here that have page-wide scope that are not brought in from other modules; that are created here and used here ]] local added_deprecated_cat; -- Boolean flag so that the category is added only once local added_vanc_errs; -- Boolean flag so we only emit one Vancouver error / category local added_generic_name_errs; -- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered local Frame; -- holds the module's frame table local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module) local is_sandbox; -- true when using sandbox modules to render citation --[[--------------------------< F I R S T _ S E T >------------------------------------------------------------ Locates and returns the first set value in a table of values where the order established in the table, left-to-right (or top-to-bottom), is the order in which the values are evaluated. Returns nil if none are set. This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs. With the pairs version the order of evaluation could not be guaranteed. With the ipairs version, a nil value would terminate the for-loop before it reached the actual end of the list. ]] local function first_set (list, count) local i = 1; while i <= count do -- loop through all items in list if utilities.is_set( list[i] ) then return list[i]; -- return the first set list member end i = i + 1; -- point to next end end --[[--------------------------< A D D _ V A N C _ E R R O R >---------------------------------------------------- Adds a single Vancouver system error message to the template's output regardless of how many error actually exist. To prevent duplication, added_vanc_errs is nil until an error message is emitted. added_vanc_errs is a Boolean declared in page scope variables above ]] local function add_vanc_error (source, position) if added_vanc_errs then return end added_vanc_errs = true; -- note that we've added this category utilities.set_message ('err_vancouver', {source, position}); end --[[--------------------------< I S _ S C H E M E >------------------------------------------------------------ does this thing that purports to be a URI scheme seem to be a valid scheme? The scheme is checked to see if it is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says: Scheme names consist of a sequence of characters beginning with a letter and followed by any combination of letters, digits, plus ("+"), period ("."), or hyphen ("-"). returns true if it does, else false ]] local function is_scheme (scheme) return scheme and scheme:match ('^%a[%a%d%+%.%-]*:'); -- true if scheme is set and matches the pattern end --[=[-------------------------< I S _ D O M A I N _ N A M E >-------------------------------------------------- Does this thing that purports to be a domain name seem to be a valid domain name? Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5 BNF defined here: https://tools.ietf.org/html/rfc4234 Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15; see also [[Single-letter second-level domain]] list of TLDs: https://www.iana.org/domains/root/db RFC 952 (modified by RFC 1123) requires the first and last character of a hostname to be a letter or a digit. Between the first and last characters the name may use letters, digits, and the hyphen. Also allowed are IPv4 addresses. IPv6 not supported domain is expected to be stripped of any path so that the last character in the last character of the TLD. tld is two or more alpha characters. Any preceding '//' (from splitting a URL with a scheme) will be stripped here. Perhaps not necessary but retained in case it is necessary for IPv4 dot decimal. There are several tests: the first character of the whole domain name including subdomains must be a letter or a digit internationalized domain name (ASCII characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the TLD) see https://tools.ietf.org/html/rfc3490 single-letter/digit second-level domains in the .org, .cash, and .today TLDs q, x, and z SL domains in the .com TLD i and q SL domains in the .net TLD single-letter SL domains in the ccTLDs (where the ccTLD is two letters) two-character SL domains in gTLDs (where the gTLD is two or more letters) three-plus-character SL domains in gTLDs (where the gTLD is two or more letters) IPv4 dot-decimal address format; TLD not allowed returns true if domain appears to be a proper name and TLD or IPv4 address, else false ]=] local function is_domain_name (domain) if not domain then return false; -- if not set, abandon end domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once if not domain:match ('^[%w]') then -- first character must be letter or digit return false; end if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at Wikisource return false; end local patterns = { -- patterns that look like URLs '%f[%w][%w][%w%-]+[%w]%.%a%a+$', -- three or more character hostname.hostname or hostname.tld '%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$', -- internationalized domain name with ACE prefix '%f[%a][qxz]%.com$', -- assigned one character .com hostname (x.com times out 2015-12-10) '%f[%a][iq]%.net$', -- assigned one character .net hostname (q.net registered but not active 2015-12-10) '%f[%w][%w]%.%a%a$', -- one character hostname and ccTLD (2 chars) '%f[%w][%w][%w]%.%a%a+$', -- two character hostname and TLD '^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?', -- IPv4 address } for _, pattern in ipairs (patterns) do -- loop through the patterns list if domain:match (pattern) then return true; -- if a match then we think that this thing that purports to be a URL is a URL end end for _, d in ipairs (cfg.single_letter_2nd_lvl_domains_t) do -- look for single letter second level domain names for these top level domains if domain:match ('%f[%w][%w]%.' .. d) then return true end end return false; -- no matches, we don't know what this thing is end --[[--------------------------< I S _ U R L >------------------------------------------------------------------ returns true if the scheme and domain parts of a URL appear to be a valid URL; else false. This function is the last step in the validation process. This function is separate because there are cases that are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external wikilinks. ]] local function is_url (scheme, domain) if utilities.is_set (scheme) then -- if scheme is set check it and domain return is_scheme (scheme) and is_domain_name (domain); else return is_domain_name (domain); -- scheme not set when URL is protocol-relative end end --[[--------------------------< S P L I T _ U R L >------------------------------------------------------------ Split a URL into a scheme, authority indicator, and domain. First remove Fully Qualified Domain Name terminator (a dot following TLD) (if any) and any path(/), query(?) or fragment(#). If protocol-relative URL, return nil scheme and domain else return nil for both scheme and domain. When not protocol-relative, get scheme, authority indicator, and domain. If there is an authority indicator (one or more '/' characters immediately following the scheme's colon), make sure that there are only 2. Any URL that does not have news: scheme must have authority indicator (//). TODO: are there other common schemes like news: that don't use authority indicator? Strip off any port and path; ]] local function split_url (url_str) local scheme, authority, domain; url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1'); -- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//') if url_str:match ('^//%S*') then -- if there is what appears to be a protocol-relative URL domain = url_str:match ('^//(%S*)') elseif url_str:match ('%S-:/*%S+') then -- if there is what appears to be a scheme, optional authority indicator, and domain name scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)'); -- extract the scheme, authority indicator, and domain portions if utilities.is_set (authority) then authority = authority:gsub ('//', '', 1); -- replace place 1 pair of '/' with nothing; if utilities.is_set(authority) then -- if anything left (1 or 3+ '/' where authority should be) then return scheme; -- return scheme only making domain nil which will cause an error message end else if not scheme:match ('^news:') then -- except for news:..., MediaWiki won't link URLs that do not have authority indicator; TODO: a better way to do this test? return scheme; -- return scheme only making domain nil which will cause an error message end end domain = domain:gsub ('(%a):%d+', '%1'); -- strip port number if present end return scheme, domain; end --[[--------------------------< L I N K _ P A R A M _ O K >--------------------------------------------------- checks the content of |title-link=, |series-link=, |author-link=, etc. for properly formatted content: no wikilinks, no URLs Link parameters are to hold the title of a Wikipedia article, so none of the WP:TITLESPECIALCHARACTERS are allowed: # < > [ ] | { } _ except the underscore which is used as a space in wiki URLs and # which is used for section links returns false when the value contains any of these characters. When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid URL (the |<param>-link= parameter is ok); else false when value appears to be a valid URL (the |<param>-link= parameter is NOT ok). ]] local function link_param_ok (value) local scheme, domain; if value:find ('[<>%[%]|{}]') then -- if any prohibited characters return false; end scheme, domain = split_url (value); -- get scheme or nil and domain or nil from URL; return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid URL end --[[--------------------------< L I N K _ T I T L E _ O K >--------------------------------------------------- Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value. |<title>= may be wiki-linked but not when |<param>-link= has a value. This function emits an error message when that condition exists check <link> for inter-language interwiki-link prefix. prefix must be a MediaWiki-recognized language code and must begin with a colon. ]] local function link_title_ok (link, lorig, title, torig) local orig; if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value if not link_param_ok (link) then -- check |<param>-link= markup orig = lorig; -- identify the failing link parameter elseif title:find ('%[%[') then -- check |title= for wikilink markup orig = torig; -- identify the failing |title= parameter elseif link:match ('^%a+:') then -- if the link is what looks like an interwiki local prefix = link:match ('^(%a+):'):lower(); -- get the interwiki prefix if cfg.inter_wiki_map[prefix] then -- if prefix is in the map, must have preceding colon orig = lorig; -- flag as error end end end if utilities.is_set (orig) then link = ''; -- unset utilities.set_message ('err_bad_paramlink', orig); -- URL or wikilink in |title= with |title-link=; end return link; -- link if ok, empty string else end --[[--------------------------< C H E C K _ U R L >------------------------------------------------------------ Determines whether a URL string appears to be valid. First we test for space characters. If any are found, return false. Then split the URL into scheme and domain portions, or for protocol-relative (//example.com) URLs, just the domain. Use is_url() to validate the two portions of the URL. If both are valid, or for protocol-relative if domain is valid, return true, else false. Because it is different from a standard URL, and because this module used external_link() to make external links that work for standard and news: links, we validate newsgroup names here. The specification for a newsgroup name is at https://tools.ietf.org/html/rfc5536#section-3.1.4 ]] local function check_url( url_str ) if nil == url_str:match ("^%S+$") then -- if there are any spaces in |url=value it can't be a proper URL return false; end local scheme, domain; scheme, domain = split_url (url_str); -- get scheme or nil and domain or nil from URL; if 'news:' == scheme then -- special case for newsgroups return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$'); end return is_url (scheme, domain); -- return true if value appears to be a valid URL end --[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >---------------------------- Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first non-space characters following the opening bracket appear to be a URL. The test will also find external wikilinks that use protocol-relative URLs. Also finds bare URLs. The frontier pattern prevents a match on interwiki-links which are similar to scheme:path URLs. The tests that find bracketed URLs are required because the parameters that call this test (currently |title=, |chapter=, |work=, and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]] is possible as might be [[en://Hus]]. ]=] local function is_parameter_ext_wikilink (value) local scheme, domain; if value:match ('%f[%[]%[%a%S*:%S+.*%]') then -- if ext. wikilink with scheme and domain: [xxxx://yyyyy.zzz] scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]')); elseif value:match ('%f[%[]%[//%S+.*%]') then -- if protocol-relative ext. wikilink: [//yyyyy.zzz] scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]')); elseif value:match ('%a%S*:%S+') then -- if bare URL with scheme; may have leading or trailing plain text scheme, domain = split_url (value:match ('(%a%S*:%S+)')); elseif value:match ('//%S+') then -- if protocol-relative bare URL: //yyyyy.zzz; may have leading or trailing plain text scheme, domain = split_url (value:match ('(//%S+)')); -- what is left should be the domain else return false; -- didn't find anything that is obviously a URL end return is_url (scheme, domain); -- return true if value appears to be a valid URL end --[[-------------------------< C H E C K _ F O R _ U R L >----------------------------------------------------- loop through a list of parameters and their values. Look at the value and if it has an external link, emit an error message. ]] local function check_for_url (parameter_list, error_list) for k, v in pairs (parameter_list) do -- for each parameter in the list if is_parameter_ext_wikilink (v) then -- look at the value; if there is a URL add an error message table.insert (error_list, utilities.wrap_style ('parameter', k)); end end end --[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------ Escape sequences for content that will be used for URL descriptions ]] local function safe_for_url( str ) if str:match( "%[%[.-%]%]" ) ~= nil then utilities.set_message ('err_wikilink_in_url', {}); end return str:gsub( '[%[%]\n]', { ['['] = '&#91;', [']'] = '&#93;', ['\n'] = ' ' } ); end --[[--------------------------< E X T E R N A L _ L I N K >---------------------------------------------------- Format an external link with error checking ]] local function external_link (URL, label, source, access) local err_msg = ''; local domain; local path; local base_url; if not utilities.is_set (label) then label = URL; if utilities.is_set (source) then utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)}); else error (cfg.messages["bare_url_no_origin"]); -- programmer error; valid parameter name does not have matching meta-parameter end end if not check_url (URL) then utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); end domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$'); -- split the URL into scheme plus domain and path if path then -- if there is a path portion path = path:gsub ('[%[%]]', {['['] = '%5b', [']'] = '%5d'}); -- replace '[' and ']' with their percent-encoded values URL = table.concat ({domain, path}); -- and reassemble end base_url = table.concat ({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wiki-markup URL if utilities.is_set (access) then -- access level (subscription, registration, limited) base_url = utilities.substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon end return base_url; end --[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >-------------------------------------- Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated parameters in the citation. added_deprecated_cat is a Boolean declared in page scope variables above ]] local function deprecated_parameter(name) if not added_deprecated_cat then added_deprecated_cat = true; -- note that we've added this category utilities.set_message ('err_deprecated_params', {name}); -- add error message end end --[=[-------------------------< K E R N _ Q U O T E S >-------------------------------------------------------- Apply kerning to open the space between the quote mark provided by the module and a leading or trailing quote mark contained in a |title= or |chapter= parameter's value. This function will positive kern either single or double quotes: "'Unkerned title with leading and trailing single quote marks'" " 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example) Double single quotes (italic or bold wiki-markup) are not kerned. Replaces Unicode quote marks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter quote marks regardless of the need for kerning. Unicode quote marks are not replaced in simple [[D]] wikilinks. Call this function for chapter titles, for website titles, etc.; not for book titles. ]=] local function kern_quotes (str) local cap = ''; local wl_type, label, link; wl_type, label, link = utilities.is_wikilink (str); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]] if 1 == wl_type then -- [[D]] simple wikilink with or without quote marks if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then -- leading and trailing quote marks str = utilities.substitute (cfg.presentation['kern-left'], str); str = utilities.substitute (cfg.presentation['kern-right'], str); elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]') then -- leading quote marks str = utilities.substitute (cfg.presentation['kern-left'], str); elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then -- trailing quote marks str = utilities.substitute (cfg.presentation['kern-right'], str); end else -- plain text or [[L|D]]; text in label variable label = mw.ustring.gsub (label, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark) label = mw.ustring.gsub (label, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark) cap = mw.ustring.match (label, "^([\"\'][^\'].+)"); -- match leading double or single quote but not doubled single quotes (italic markup) if utilities.is_set (cap) then label = utilities.substitute (cfg.presentation['kern-left'], cap); end cap = mw.ustring.match (label, "^(.+[^\'][\"\'])$") -- match trailing double or single quote but not doubled single quotes (italic markup) if utilities.is_set (cap) then label = utilities.substitute (cfg.presentation['kern-right'], cap); end if 2 == wl_type then str = utilities.make_wikilink (link, label); -- reassemble the wikilink else str = label; end end return str; end --[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >---------------------------------------- |script-title= holds title parameters that are not written in Latin-based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should not be italicized and may be written right-to-left. The value supplied by |script-title= is concatenated onto Title after Title has been wrapped in italic markup. Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate RTL languages from the English left to right. |script-title= provides a unique feature. The value in |script-title= may be prefixed with a two-character ISO 639-1 language code and a colon: |script-title=ja:*** *** (where * represents a Japanese character) Spaces between the two-character code and the colon and the colon and the first script character are allowed: |script-title=ja : *** *** |script-title=ja: *** *** |script-title=ja :*** *** Spaces preceding the prefix are allowed: |script-title = ja:*** *** The prefix is checked for validity. If it is a valid ISO 639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can know the language the tag contains. This may help the browser render the script more correctly. If the prefix is invalid, the lang attribute is not added. At this time there is no error message for this condition. Supports |script-title=, |script-chapter=, |script-<periodical>= ]] local function format_script_value (script_value, script_param) local lang=''; -- initialize to empty string local name; if script_value:match('^%l%l%l?%s*:') then -- if first 3 or 4 non-space characters are script language prefix lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script if not utilities.is_set (lang) then utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing title part']}); -- prefix without 'title'; add error message return ''; -- script_value was just the prefix so return empty string end -- if we get this far we have prefix and script name = cfg.lang_code_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code? script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script -- is prefix one of these language codes? if utilities.in_array (lang, cfg.script_lang_codes) then utilities.add_prop_cat ('script', {name, lang}) else utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['unknown language code']}); -- unknown script-language; add error message end lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute else utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['invalid language code']}); -- invalid language code; add error message lang = ''; -- invalid so set lang to empty string end else utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing prefix']}); -- no language code prefix; add error message end script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL return script_value; end --[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------ Initially for |title= and |script-title=, this function concatenates those two parameter values after the script value has been wrapped in <bdi> tags. ]] local function script_concatenate (title, script, script_param) if utilities.is_set (script) then script = format_script_value (script, script_param); -- <bdi> tags, lang attribute, categorization, etc.; returns empty string on error if utilities.is_set (script) then title = title .. ' ' .. script; -- concatenate title and script title end end return title; end --[[--------------------------< W R A P _ M S G >-------------------------------------------------------------- Applies additional message text to various parameter values. Supplied string is wrapped using a message_list configuration taking one argument. Supports lower case text for {{citation}} templates. Additional text taken from citation_config.messages - the reason this function is similar to but separate from wrap_style(). ]] local function wrap_msg (key, str, lower) if not utilities.is_set ( str ) then return ""; end if true == lower then local msg; msg = cfg.messages[key]:lower(); -- set the message to lower case before return utilities.substitute ( msg, str ); -- including template text else return utilities.substitute ( cfg.messages[key], str ); end end --[[----------------< W I K I S O U R C E _ U R L _ M A K E >------------------- Makes a Wikisource URL from Wikisource interwiki-link. Returns the URL and appropriate label; nil else. str is the value assigned to |chapter= (or aliases) or |title= or |title-link= ]] local function wikisource_url_make (str) local wl_type, D, L; local ws_url, ws_label; local wikisource_prefix = table.concat ({'https://', cfg.this_wiki_code, '.wikisource.org/wiki/'}); wl_type, D, L = utilities.is_wikilink (str); -- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink) if 0 == wl_type then -- not a wikilink; might be from |title-link= str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace if utilities.is_set (str) then ws_url = table.concat ({ -- build a Wikisource URL wikisource_prefix, -- prefix str, -- article title }); ws_label = str; -- label for the URL end elseif 1 == wl_type then -- simple wikilink: [[Wikisource:ws article]] str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace if utilities.is_set (str) then ws_url = table.concat ({ -- build a Wikisource URL wikisource_prefix, -- prefix str, -- article title }); ws_label = str; -- label for the URL end elseif 2 == wl_type then -- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]]) str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace if utilities.is_set (str) then ws_label = D; -- get ws article name from display portion of interwiki link ws_url = table.concat ({ -- build a Wikisource URL wikisource_prefix, -- prefix str, -- article title without namespace from link portion of wikilink }); end end if ws_url then ws_url = mw.uri.encode (ws_url, 'WIKI'); -- make a usable URL ws_url = ws_url:gsub ('%%23', '#'); -- undo percent-encoding of fragment marker end return ws_url, ws_label, L or D; -- return proper URL or nil and a label or nil end --[[----------------< F O R M A T _ P E R I O D I C A L >----------------------- Format the three periodical parameters: |script-<periodical>=, |<periodical>=, and |trans-<periodical>= into a single Periodical meta-parameter. ]] local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical) if not utilities.is_set (periodical) then periodical = ''; -- to be safe for concatenation else periodical = utilities.wrap_style ('italic-title', periodical); -- style end periodical = script_concatenate (periodical, script_periodical, script_periodical_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped if utilities.is_set (trans_periodical) then trans_periodical = utilities.wrap_style ('trans-italic-title', trans_periodical); if utilities.is_set (periodical) then periodical = periodical .. ' ' .. trans_periodical; else -- here when trans-periodical without periodical or script-periodical periodical = trans_periodical; utilities.set_message ('err_trans_missing_title', {'periodical'}); end end return periodical; end --[[------------------< F O R M A T _ C H A P T E R _ T I T L E >--------------- Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=, and |chapter-url= into a single chapter meta- parameter (chapter_url_source used for error messages). ]] local function format_chapter_title (script_chapter, script_chapter_source, chapter, chapter_source, trans_chapter, trans_chapter_source, chapter_url, chapter_url_source, no_quotes, access) local ws_url, ws_label, L = wikisource_url_make (chapter); -- make a wikisource URL and label from a wikisource interwiki link if ws_url then ws_label = ws_label:gsub ('_', ' '); -- replace underscore separators with space characters chapter = ws_label; end if not utilities.is_set (chapter) then chapter = ''; -- to be safe for concatenation else if false == no_quotes then chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from module provided quote marks chapter = utilities.wrap_style ('quoted-title', chapter); end end chapter = script_concatenate (chapter, script_chapter, script_chapter_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped if utilities.is_set (chapter_url) then chapter = external_link (chapter_url, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate elseif ws_url then chapter = external_link (ws_url, chapter .. '&nbsp;', 'ws link in chapter'); -- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this? chapter = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter}); end if utilities.is_set (trans_chapter) then trans_chapter = utilities.wrap_style ('trans-quoted-title', trans_chapter); if utilities.is_set (chapter) then chapter = chapter .. ' ' .. trans_chapter; else -- here when trans_chapter without chapter or script-chapter chapter = trans_chapter; chapter_source = trans_chapter_source:match ('trans%-?(.+)'); -- when no chapter, get matching name from trans-<param> utilities.set_message ('err_trans_missing_title', {chapter_source}); end end return chapter; end --[[----------------< H A S _ I N V I S I B L E _ C H A R S >------------------- This function searches a parameter's value for non-printable or invisible characters. The search stops at the first match. This function will detect the visible replacement character when it is part of the Wikisource. Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers (gallery, math, pre, ref) and identifies them with a slightly different error message. See also coins_cleanup(). Output of this function is an error message that identifies the character or the Unicode group, or the stripmarker that was detected along with its position (or, for multi-byte characters, the position of its first byte) in the parameter value. ]] local function has_invisible_chars (param, v) local position = ''; -- position of invisible char or starting position of stripmarker local capture; -- used by stripmarker detection to hold name of the stripmarker local stripmarker; -- boolean set true when a stripmarker is found capture = string.match (v, '[%w%p ]*'); -- test for values that are simple ASCII text and bypass other tests if true if capture == v then -- if same there are no Unicode characters return; end for _, invisible_char in ipairs (cfg.invisible_chars) do local char_name = invisible_char[1]; -- the character or group name local pattern = invisible_char[2]; -- the pattern used to find it position, _, capture = mw.ustring.find (v, pattern); -- see if the parameter value contains characters that match the pattern if position and (cfg.invisible_defs.zwj == capture) then -- if we found a zero-width joiner character if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts position = nil; -- unset position elseif cfg.emoji_t[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}? position = nil; -- unset position end end if position then if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition) ('templatestyles' == capture and utilities.in_array (param, {'id', 'quote'})) then -- templatestyles stripmarker allowed in these parameters stripmarker = true; -- set a flag elseif true == stripmarker and cfg.invisible_defs.del == capture then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker position = nil; -- unset else local err_msg; if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then err_msg = capture .. ' ' .. char_name; else err_msg = char_name .. ' ' .. 'character'; end utilities.set_message ('err_invisible_char', {err_msg, utilities.wrap_style ('parameter', param), position}); -- add error message return; -- and done with this parameter end end end end --[[-------------------< A R G U M E N T _ W R A P P E R >---------------------- Argument wrapper. This function provides support for argument mapping defined in the configuration file so that multiple names can be transparently aliased to single internal variable. ]] local function argument_wrapper ( args ) local origin = {}; return setmetatable({ ORIGIN = function ( self, k ) local dummy = self[k]; -- force the variable to be loaded. return origin[k]; end }, { __index = function ( tbl, k ) if origin[k] ~= nil then return nil; end local args, list, v = args, cfg.aliases[k]; if type( list ) == 'table' then v, origin[k] = utilities.select_one ( args, list, 'err_redundant_parameters' ); if origin[k] == nil then origin[k] = ''; -- Empty string, not nil end elseif list ~= nil then v, origin[k] = args[list], list; else -- maybe let through instead of raising an error? -- v, origin[k] = args[k], k; error( cfg.messages['unknown_argument_map'] .. ': ' .. k); end -- Empty strings, not nil; if v == nil then v = ''; origin[k] = ''; end tbl = rawset( tbl, k, v ); return v; end, }); end --[[--------------------------< N O W R A P _ D A T E >------------------------- When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>. When date is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span: <span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY DOES NOT yet support MMMM YYYY or any of the date ranges. ]] local function nowrap_date (date) local cap = ''; local cap2 = ''; if date:match("^%d%d%d%d%-%d%d%-%d%d$") then date = utilities.substitute (cfg.presentation['nowrap1'], date); elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$"); date = utilities.substitute (cfg.presentation['nowrap2'], {cap, cap2}); end return date; end --[[--------------------------< S E T _ T I T L E T Y P E >--------------------- This function sets default title types (equivalent to the citation including |type=<default value>) for those templates that have defaults. Also handles the special case where it is desirable to omit the title type from the rendered citation (|type=none). ]] local function set_titletype (cite_class, title_type) if utilities.is_set (title_type) then if 'none' == cfg.keywords_xlate[title_type] then title_type = ''; -- if |type=none then type parameter not displayed end return title_type; -- if |type= has been set to any other value use that value end return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation end --[[--------------------------< S A F E _ J O I N >----------------------------- Joins a sequence of strings together while checking for duplicate separation characters. ]] local function safe_join( tbl, duplicate_char ) local f = {}; -- create a function table appropriate to type of 'duplicate character' if 1 == #duplicate_char then -- for single byte ASCII characters use the string library functions f.gsub = string.gsub f.match = string.match f.sub = string.sub else -- for multi-byte characters use the ustring library functions f.gsub = mw.ustring.gsub f.match = mw.ustring.match f.sub = mw.ustring.sub end local str = ''; -- the output string local comp = ''; -- what does 'comp' mean? local end_chr = ''; local trim; for _, value in ipairs( tbl ) do if value == nil then value = ''; end if str == '' then -- if output string is empty str = value; -- assign value to it (first time through the loop) elseif value ~= '' then if value:sub(1, 1) == '<' then -- special case of values enclosed in spans and other markup. comp = value:gsub( "%b<>", "" ); -- remove HTML markup (<span>string</span> -> string) else comp = value; end -- typically duplicate_char is sepc if f.sub(comp, 1, 1) == duplicate_char then -- is first character same as duplicate_char? why test first character? -- Because individual string segments often (always?) begin with terminal punct for the -- preceding segment: 'First element' .. 'sepc next element' .. etc.? trim = false; end_chr = f.sub(str, -1, -1); -- get the last character of the output string -- str = str .. "<HERE(enchr=" .. end_chr .. ")" -- debug stuff? if end_chr == duplicate_char then -- if same as separator str = f.sub(str, 1, -2); -- remove it elseif end_chr == "'" then -- if it might be wiki-markup if f.sub(str, -3, -1) == duplicate_char .. "''" then -- if last three chars of str are sepc'' str = f.sub(str, 1, -4) .. "''"; -- remove them and add back '' elseif f.sub(str, -5, -1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]'' trim = true; -- why? why do this and next differently from previous? elseif f.sub(str, -4, -1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]'' trim = true; -- same question end elseif end_chr == "]" then -- if it might be wiki-markup if f.sub(str, -3, -1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink trim = true; elseif f.sub(str, -3, -1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link trim = true; elseif f.sub(str, -2, -1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link trim = true; elseif f.sub(str, -4, -1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title. trim = true; end elseif end_chr == " " then -- if last char of output string is a space if f.sub(str, -2, -1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space> str = f.sub(str, 1, -3); -- remove them both end end if trim then if value ~= comp then -- value does not equal comp when value contains HTML markup local dup2 = duplicate_char; if f.match(dup2, "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows HTML markup else value = f.sub(value, 2, -1 ); -- remove duplicate_char when it is first character end end end str = str .. value; -- add it to the output string end end return str; end --[[--------------------------< I S _ S U F F I X >----------------------------- returns true if suffix is properly formed Jr, Sr, or ordinal in the range 1–9. Puncutation not allowed. ]] local function is_suffix (suffix) if utilities.in_array (suffix, {'Jr', 'Sr', 'Jnr', 'Snr', '1st', '2nd', '3rd'}) or suffix:match ('^%dth$') then return true; end return false; end --[[--------------------< I S _ G O O D _ V A N C _ N A M E >------------------- For Vancouver style, author/editor names are supposed to be rendered in Latin (read ASCII) characters. When a name uses characters that contain diacritical marks, those characters are to be converted to the corresponding Latin character. When a name is written using a non-Latin alphabet or logogram, that name is to be transliterated into Latin characters. The module doesn't do this so editors may/must. This test allows |first= and |last= names to contain any of the letters defined in the four Unicode Latin character sets [http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A [http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF [http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F [http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F |lastn= also allowed to contain hyphens, spaces, and apostrophes. (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) |firstn= also allowed to contain hyphens, spaces, apostrophes, and periods This original test: if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]+[2-6%a]*$") then was written outside of the code editor and pasted here because the code editor gets confused between character insertion point and cursor position. The test has been rewritten to use decimal character escape sequence for the individual bytes of the Unicode characters so that it is not necessary to use an external editor to maintain this code. \195\128-\195\150 – À-Ö (U+00C0–U+00D6 – C0 controls) \195\152-\195\182 – Ø-ö (U+00D8-U+00F6 – C0 controls) \195\184-\198\191 – ø-ƿ (U+00F8-U+01BF – C0 controls, Latin extended A & B) \199\132-\201\143 – DŽ-ɏ (U+01C4-U+024F – Latin extended B) ]] local function is_good_vanc_name (last, first, suffix, position) if not suffix then if first:find ('[,%s]') then -- when there is a space or comma, might be first name/initials + generational suffix first = first:match ('(.-)[,%s]+'); -- get name/initials suffix = first:match ('[,%s]+(.+)$'); -- get generational suffix end end if utilities.is_set (suffix) then if not is_suffix (suffix) then add_vanc_error (cfg.err_msg_supl.suffix, position); return false; -- not a name with an appropriate suffix end end if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%'%.]*$") then add_vanc_error (cfg.err_msg_supl['non-Latin char'], position); return false; -- not a string of Latin characters; Vancouver requires Romanization end; return true; end --[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------ Attempts to convert names to initials in support of |name-list-style=vanc. Names in |firstn= may be separated by spaces or hyphens, or for initials, a period. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/. Vancouver style requires family rank designations (Jr, II, III, etc.) to be rendered as Jr, 2nd, 3rd, etc. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/. This code only accepts and understands generational suffix in the Vancouver format because Roman numerals look like, and can be mistaken for, initials. This function uses ustring functions because firstname initials may be any of the Unicode Latin characters accepted by is_good_vanc_name (). ]] local function reduce_to_initials(first, position) local name, suffix = mw.ustring.match(first, "^(%u+) ([%dJS][%drndth]+)$"); if not name then -- if not initials and a suffix name = mw.ustring.match(first, "^(%u+)$"); -- is it just initials? end if name then -- if first is initials with or without suffix if 3 > mw.ustring.len (name) then -- if one or two initials if suffix then -- if there is a suffix if is_suffix (suffix) then -- is it legitimate? return first; -- one or two initials and a valid suffix so nothing to do else add_vanc_error (cfg.err_msg_supl.suffix, position); -- one or two initials with invalid suffix so error message return first; -- and return first unmolested end else return first; -- one or two initials without suffix; nothing to do end end end -- if here then name has 3 or more uppercase letters so treat them as a word local initials, names = {}, {}; -- tables to hold name parts and initials local i = 1; -- counter for number of initials names = mw.text.split (first, '[%s,]+'); -- split into a table of names and possible suffix while names[i] do -- loop through the table if 1 < i and names[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot) names[i] = names[i]:gsub ('%.', ''); -- remove terminal dot if present if is_suffix (names[i]) then -- if a legitimate suffix table.insert (initials, ' ' .. names[i]); -- add a separator space, insert at end of initials table break; -- and done because suffix must fall at the end of a name end -- no error message if not a suffix; possibly because of Romanization end if 3 > i then table.insert (initials, mw.ustring.sub(names[i], 1, 1)); -- insert the initial at end of initials table end i = i + 1; -- bump the counter end return table.concat(initials) -- Vancouver format does not include spaces. end --[[--------------------------< I N T E R W I K I _ P R E F I X E N _ G E T >---------------------------------- extract interwiki prefixen from <value>. Returns two one or two values: false – no prefixen nil – prefix exists but not recognized project prefix, language prefix – when value has either of: :<project>:<language>:<article> :<language>:<project>:<article> project prefix, nil – when <value> has only a known single-letter prefix nil, language prefix – when <value> has only a known language prefix accepts single-letter project prefixen: 'd' (wikidata), 's' (wikisource), and 'w' (wikipedia) prefixes; at this writing, the other single-letter prefixen (b (wikibook), c (commons), m (meta), n (wikinews), q (wikiquote), and v (wikiversity)) are not supported. ]] local function interwiki_prefixen_get (value, is_link) if not value:find (':%l+:') then -- if no prefix return false; -- abandon; boolean here to distinguish from nil fail returns later end local prefix_patterns_linked_t = { -- sequence of valid interwiki and inter project prefixen '^%[%[:([dsw]):(%l%l+):', -- wikilinked; project and language prefixes '^%[%[:(%l%l+):([dsw]):', -- wikilinked; language and project prefixes '^%[%[:([dsw]):', -- wikilinked; project prefix '^%[%[:(%l%l+):', -- wikilinked; language prefix } local prefix_patterns_unlinked_t = { -- sequence of valid interwiki and inter project prefixen '^:([dsw]):(%l%l+):', -- project and language prefixes '^:(%l%l+):([dsw]):', -- language and project prefixes '^:([dsw]):', -- project prefix '^:(%l%l+):', -- language prefix } local cap1, cap2; for _, pattern in ipairs ((is_link and prefix_patterns_linked_t) or prefix_patterns_unlinked_t) do cap1, cap2 = value:match (pattern); if cap1 then break; -- found a match so stop looking end end if cap1 and cap2 then -- when both then :project:language: or :language:project: (both forms allowed) if 1 == #cap1 then -- length == 1 then :project:language: if cfg.inter_wiki_map[cap2] then -- is language prefix in the interwiki map? return cap1, cap2; -- return interwiki project and interwiki language end else -- here when :language:project: if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map? return cap2, cap1; -- return interwiki project and interwiki language end end return nil; -- unknown interwiki language elseif not (cap1 or cap2) then -- both are nil? return nil; -- we got something that looks like a project prefix but isn't; return fail elseif 1 == #cap1 then -- here when one capture return cap1, nil; -- length is 1 so return project, nil language else -- here when one capture and its length it more than 1 if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map? return nil, cap1; -- return nil project, language end end end --[[--------------------------< L I S T _ P E O P L E >-------------------------- Formats a list of people (authors, contributors, editors, interviewers, translators) names in the list will be linked when |<name>-link= has a value |<name>-mask- does NOT have a value; masked names are presumed to have been rendered previously so should have been linked there when |<name>-mask=0, the associated name is not rendered ]] local function list_people (control, people, etal) local sep; local namesep; local format = control.format; local maximum = control.maximum; local name_list = {}; if 'vanc' == format then -- Vancouver-like name styling? sep = cfg.presentation['sep_nl_vanc']; -- name-list separator between names is a comma namesep = cfg.presentation['sep_name_vanc']; -- last/first separator is a space else sep = cfg.presentation['sep_nl']; -- name-list separator between names is a semicolon namesep = cfg.presentation['sep_name']; -- last/first separator is <comma><space> end if sep:sub (-1, -1) ~= " " then sep = sep .. " " end if utilities.is_set (maximum) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for other names for i, person in ipairs (people) do if utilities.is_set (person.last) then local mask = person.mask; local one; local sep_one = sep; if utilities.is_set (maximum) and i > maximum then etal = true; break; end if mask then local n = tonumber (mask); -- convert to a number if it can be converted; nil else if n then one = 0 ~= n and string.rep("&mdash;", n) or nil; -- make a string of (n > 0) mdashes, nil else, to replace name person.link = nil; -- don't create link to name if name is replaces with mdash string or has been set nil else one = mask; -- replace name with mask text (must include name-list separator) sep_one = " "; -- modify name-list separator end else one = person.last; -- get surname local first = person.first -- get given name if utilities.is_set (first) then if ("vanc" == format) then -- if Vancouver format one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) if not person.corporate and is_good_vanc_name (one, first, nil, i) then -- and name is all Latin characters; corporate authors not tested first = reduce_to_initials (first, i); -- attempt to convert first name(s) to initials end end one = one .. namesep .. first; end end if utilities.is_set (person.link) then one = utilities.make_wikilink (person.link, one); -- link author/editor end if one then -- if <one> has a value (name, mdash replacement, or mask text replacement) local proj, tag = interwiki_prefixen_get (one, true); -- get the interwiki prefixen if present if 'w' == proj and ('Wikipedia' == mw.site.namespaces.Project['name']) then proj = nil; -- for stuff like :w:de:<article>, :w is unnecessary TODO: maint cat? end if proj then proj = ({['d'] = 'Wikidata', ['s'] = 'Wikisource', ['w'] = 'Wikipedia'})[proj]; -- :w (wikipedia) for linking from a non-wikipedia project if proj then one = one .. utilities.wrap_style ('interproj', proj); -- add resized leading space, brackets, static text, language name tag = nil; -- unset; don't do both project and language end end if tag == cfg.this_wiki_code then tag = nil; -- stuff like :en:<article> at en.wiki is pointless TODO: maint cat? end if tag then local lang = cfg.lang_code_remap[tag] or cfg.mw_languages_by_tag_t[tag]; if lang then -- error messaging done in extract_names() where we know parameter names one = one .. utilities.wrap_style ('interwiki', lang); -- add resized leading space, brackets, static text, language name end end table.insert (name_list, one); -- add it to the list of names table.insert (name_list, sep_one); -- add the proper name-list separator end end end local count = #name_list / 2; -- (number of names + number of separators) divided by 2 if 0 < count then if 1 < count and not etal then if 'amp' == format then name_list[#name_list-2] = " & "; -- replace last separator with ampersand text elseif 'and' == format then if 2 == count then name_list[#name_list-2] = cfg.presentation.sep_nl_and; -- replace last separator with 'and' text else name_list[#name_list-2] = cfg.presentation.sep_nl_end; -- replace last separator with '(sep) and' text end end end name_list[#name_list] = nil; -- erase the last separator end local result = table.concat (name_list); -- construct list if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list result = result .. sep .. ' ' .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al. end return result, count; -- return name-list string and count of number of names (count used for editor names only) end --[[--------------------< M A K E _ C I T E R E F _ I D >----------------------- Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise returns an empty string. namelist is one of the contributor-, author-, or editor-name lists chosen in that order. year is Year or anchor_year. ]] local function make_citeref_id (namelist, year) local names={}; -- a table for the one to four names and year for i,v in ipairs (namelist) do -- loop through the list and take up to the first four last names names[i] = v.last if i == 4 then break end -- if four then done end table.insert (names, year); -- add the year at the end local id = table.concat(names); -- concatenate names and year for CITEREF id if utilities.is_set (id) then -- if concatenation is not an empty string return "CITEREF" .. id; -- add the CITEREF portion else return ''; -- return an empty string; no reason to include CITEREF id in this citation end end --[[--------------------------< C I T E _ C L A S S _A T T R I B U T E _M A K E >------------------------------ construct <cite> tag class attribute for this citation. <cite_class> – config.CitationClass from calling template <mode> – value from |mode= parameter ]] local function cite_class_attribute_make (cite_class, mode) local class_t = {}; table.insert (class_t, 'citation'); -- required for blue highlight if 'citation' ~= cite_class then table.insert (class_t, cite_class); -- identify this template for user css table.insert (class_t, utilities.is_set (mode) and mode or 'cs1'); -- identify the citation style for user css or javascript else table.insert (class_t, utilities.is_set (mode) and mode or 'cs2'); -- identify the citation style for user css or javascript end for _, prop_key in ipairs (z.prop_keys_t) do table.insert (class_t, prop_key); -- identify various properties for user css or javascript end return table.concat (class_t, ' '); -- make a big string and done end --[[---------------------< N A M E _ H A S _ E T A L >-------------------------- Evaluates the content of name parameters (author, editor, etc.) for variations on the theme of et al. If found, the et al. is removed, a flag is set to true and the function returns the modified name and the flag. This function never sets the flag to false but returns its previous state because it may have been set by previous passes through this function or by the associated |display-<names>=etal parameter ]] local function name_has_etal (name, etal, nocat, param) if utilities.is_set (name) then -- name can be nil in which case just return local patterns = cfg.et_al_patterns; -- get patterns from configuration for _, pattern in ipairs (patterns) do -- loop through all of the patterns if name:match (pattern) then -- if this 'et al' pattern is found in name name = name:gsub (pattern, ''); -- remove the offending text etal = true; -- set flag (may have been set previously here or by |display-<names>=etal) if not nocat then -- no categorization for |vauthors= utilities.set_message ('err_etal', {param}); -- and set an error if not added end end end end return name, etal; end --[[---------------------< N A M E _ I S _ N U M E R I C >---------------------- Add maint cat when name parameter value does not contain letters. Does not catch mixed alphanumeric names so |last=A. Green (1922-1987) does not get caught in the current version of this test but |first=(1888) is caught. returns nothing ]] local function name_is_numeric (name, list_name) if utilities.is_set (name) then if mw.ustring.match (name, '^[%A]+$') then -- when name does not contain any letters utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template end end end --[[-----------------< N A M E _ H A S _ M U L T _ N A M E S >------------------ Evaluates the content of last/surname (authors etc.) parameters for multiple names. Multiple names are indicated if there is more than one comma or any "unescaped" semicolons. Escaped semicolons are ones used as part of selected HTML entities. If the condition is met, the function adds the multiple name maintenance category. returns nothing ]] local function name_has_mult_names (name, list_name) local _, commas, semicolons, nbsps; if utilities.is_set (name) then _, commas = name:gsub (',', ''); -- count the number of commas _, semicolons = name:gsub (';', ''); -- count the number of semicolons -- nbsps probably should be its own separate count rather than merged in -- some way with semicolons because Lua patterns do not support the -- grouping operator that regex does, which means there is no way to add -- more entities to escape except by adding more counts with the new -- entities _, nbsps = name:gsub ('&nbsp;',''); -- count nbsps -- There is exactly 1 semicolon per &nbsp; entity, so subtract nbsps -- from semicolons to 'escape' them. If additional entities are added, -- they also can be subtracted. if 1 < commas or 0 < (semicolons - nbsps) then utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message end end end --[=[-------------------------< I S _ G E N E R I C >---------------------------------------------------------- Compares values assigned to various parameters according to the string provided as <item> in the function call. <item> can have on of two values: 'generic_names' – for name-holding parameters: |last=, |first=, |editor-last=, etc 'generic_titles' – for |title= There are two types of generic tests. The 'accept' tests look for a pattern that should not be rejected by the 'reject' test. For example, |author=[[John Smith (author)|Smith, John]] would be rejected by the 'author' reject test. But piped wikilinks with 'author' disambiguation should not be rejected so the 'accept' test prevents that from happening. Accept tests are always performed before reject tests. Each of the 'accept' and 'reject' sequence tables hold tables for en.wiki (['en']) and local.wiki (['local']) that each can hold a test sequence table The sequence table holds, at index [1], a test pattern, and, at index [2], a boolean control value. The control value tells string.find() or mw.ustring.find() to do plain-text search (true) or a pattern search (false). The intent of all this complexity is to make these searches as fast as possible so that we don't run out of processing time on very large articles. Returns true when a reject test finds the pattern or string false when an accept test finds the pattern or string nil else ]=] local function is_generic (item, value, wiki) local test_val; local str_lower = { -- use string.lower() for en.wiki (['en']) and use mw.ustring.lower() or local.wiki (['local']) ['en'] = string.lower, ['local'] = mw.ustring.lower, } local str_find = { -- use string.find() for en.wiki (['en']) and use mw.ustring.find() or local.wiki (['local']) ['en'] = string.find, ['local'] = mw.ustring.find, } local function test (val, test_t, wiki) -- local function to do the testing; <wiki> selects lower() and find() functions val = test_t[2] and str_lower[wiki](value) or val; -- when <test_t[2]> set to 'true', plaintext search using lowercase value return str_find[wiki] (val, test_t[1], 1, test_t[2]); -- return nil when not found or matched end local test_types_t = {'accept', 'reject'}; -- test accept patterns first, then reject patterns local wikis_t = {'en', 'local'}; -- do tests for each of these keys; en.wiki first, local.wiki second for _, test_type in ipairs (test_types_t) do -- for each test type for _, generic_value in pairs (cfg.special_case_translation[item][test_type]) do -- spin through the list of generic value fragments to accept or reject for _, wiki in ipairs (wikis_t) do if generic_value[wiki] then if test (value, generic_value[wiki], wiki) then -- go do the test return ('reject' == test_type); -- param value rejected, return true; false else end end end end end end --[[--------------------------< N A M E _ I S _ G E N E R I C >------------------------------------------------ calls is_generic() to determine if <name> is a 'generic name' listed in cfg.generic_names; <name_alias> is the parameter name used in error messaging ]] local function name_is_generic (name, name_alias) if not added_generic_name_errs and is_generic ('generic_names', name) then utilities.set_message ('err_generic_name', name_alias); -- set an error message added_generic_name_errs = true; end end --[[--------------------------< N A M E _ C H E C K S >-------------------------------------------------------- This function calls various name checking functions used to validate the content of the various name-holding parameters. ]] local function name_checks (last, first, list_name, last_alias, first_alias) local accept_name; if utilities.is_set (last) then last, accept_name = utilities.has_accept_as_written (last); -- remove accept-this-as-written markup when it wraps all of <last> if not accept_name then -- <last> not wrapped in accept-as-written markup name_has_mult_names (last, list_name); -- check for multiple names in the parameter (last only) name_is_numeric (last, list_name); -- check for names that are composed of digits and punctuation name_is_generic (last, last_alias); -- check for names found in the generic names list end end if utilities.is_set (first) then first, accept_name = utilities.has_accept_as_written (first); -- remove accept-this-as-written markup when it wraps all of <first> if not accept_name then -- <first> not wrapped in accept-as-written markup name_is_numeric (first, list_name); -- check for names that are composed of digits and punctuation name_is_generic (first, first_alias); -- check for names found in the generic names list end local wl_type, D = utilities.is_wikilink (first); if 0 ~= wl_type then first = D; utilities.set_message ('err_bad_paramlink', first_alias); end end return last, first; -- done end --[[----------------------< E X T R A C T _ N A M E S >------------------------- Gets name list from the input arguments Searches through args in sequential order to find |lastn= and |firstn= parameters (or their aliases), and their matching link and mask parameters. Stops searching when both |lastn= and |firstn= are not found in args after two sequential attempts: found |last1=, |last2=, and |last3= but doesn't find |last4= and |last5= then the search is done. This function emits an error message when there is a |firstn= without a matching |lastn=. When there are 'holes' in the list of last names, |last1= and |last3= are present but |last2= is missing, an error message is emitted. |lastn= is not required to have a matching |firstn=. When an author or editor parameter contains some form of 'et al.', the 'et al.' is stripped from the parameter and a flag (etal) returned that will cause list_people() to add the static 'et al.' text from Module:Citation/CS1/Configuration. This keeps 'et al.' out of the template's metadata. When this occurs, an error is emitted. ]] local function extract_names(args, list_name) local names = {}; -- table of names local last; -- individual name components local first; local link; local mask; local i = 1; -- loop counter/indexer local n = 1; -- output table indexer local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors) local etal = false; -- return value set to true when we find some form of et al. in an author parameter local last_alias, first_alias, link_alias; -- selected parameter aliases used in error messaging while true do last, last_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'err_redundant_parameters', i ); -- search through args for name components beginning at 1 first, first_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'err_redundant_parameters', i ); link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ); mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i ); if last then -- error check |lastn= alias for unknown interwiki link prefix; done here because this is where we have the parameter name local project, language = interwiki_prefixen_get (last, true); -- true because we expect interwiki links in |lastn= to be wikilinked if nil == project and nil == language then -- when both are nil utilities.set_message ('err_bad_paramlink', last_alias); -- not known, emit an error message -- TODO: err_bad_interwiki? last = utilities.remove_wiki_link (last); -- remove wikilink markup; show display value only end end if link then -- error check |linkn= alias for unknown interwiki link prefix local project, language = interwiki_prefixen_get (link, false); -- false because wiki links in |author-linkn= is an error if nil == project and nil == language then -- when both are nil utilities.set_message ('err_bad_paramlink', link_alias); -- not known, emit an error message -- TODO: err_bad_interwiki? link = nil; -- unset so we don't link link_alias = nil; end end last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al. first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al. last, first = name_checks (last, first, list_name, last_alias, first_alias); -- multiple names, extraneous annotation, etc. checks if first and not last then -- if there is a firstn without a matching lastn local alias = first_alias:find ('given', 1, true) and 'given' or 'first'; -- get first or given form of the alias utilities.set_message ('err_first_missing_last', { first_alias, -- param name of alias missing its mate first_alias:gsub (alias, {['first'] = 'last', ['given'] = 'surname'}), -- make param name appropriate to the alias form }); -- add this error message elseif not first and not last then -- if both firstn and lastn aren't found, are we done? count = count + 1; -- number of times we haven't found last and first if 2 <= count then -- two missing names and we give up break; -- normal exit or there is a two-name hole in the list; can't tell which end else -- we have last with or without a first local result; link = link_title_ok (link, link_alias, last, last_alias); -- check for improper wiki-markup if first then link = link_title_ok (link, link_alias, first, first_alias); -- check for improper wiki-markup end names[n] = {last = last, first = first, link = link, mask = mask, corporate = false}; -- add this name to our names list (corporate for |vauthors= only) n = n + 1; -- point to next location in the names table if 1 == count then -- if the previous name was missing utilities.set_message ('err_missing_name', {list_name:match ("(%w+)List"):lower(), i - 1}); -- add this error message end count = 0; -- reset the counter, we're looking for two consecutive missing names end i = i + 1; -- point to next args location end return names, etal; -- all done, return our list of names and the etal flag end --[[--------------------------< N A M E _ T A G _ G E T >------------------------------------------------------ attempt to decode |language=<lang_param> and return language name and matching tag; nil else. This function looks for: <lang_param> as a tag in cfg.lang_code_remap{} <lang_param> as a name in cfg.lang_name_remap{} <lang_param> as a name in cfg.mw_languages_by_name_t <lang_param> as a tag in cfg.mw_languages_by_tag_t when those fail, presume that <lang_param> is an IETF-like tag that MediaWiki does not recognize. Strip all script, region, variant, whatever subtags from <lang_param> to leave just a two or three character language tag and look for the new <lang_param> in cfg.mw_languages_by_tag_t{} on success, returns name (in properly capitalized form) and matching tag (in lowercase); on failure returns nil ]] local function name_tag_get (lang_param) local lang_param_lc = mw.ustring.lower (lang_param); -- use lowercase as an index into the various tables local name; local tag; name = cfg.lang_code_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name if name then -- when <name>, <lang_param> is a tag for a remapped language name return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc> end tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags name = cfg.lang_code_remap[tag]; -- attempt to get remapped language name with language subtag only if name then -- when <name>, <tag> is a tag for a remapped language name return name, tag; -- so return <name> from remap and <tag> end if cfg.lang_name_remap[lang_param_lc] then -- not a tag, assume <lang_param_lc> is a name; attempt to get remapped language tag return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag end tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag if tag then return cfg.mw_languages_by_tag_t[tag], tag; -- <lang_param_lc> is a name so return the name from the table and <tag> end name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name if name then return name, lang_param_lc; -- <lang_param_lc> is a tag so return it and <name> end tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else if tag then name = cfg.mw_languages_by_tag_t[tag]; -- attempt to get a language name using the shortened <tag> if name then return name, tag; -- <lang_param_lc> is an unrecognized IETF-like tag so return <name> and language subtag end end end --[[-------------------< L A N G U A G E _ P A R A M E T E R >------------------ Gets language name from a provided two- or three-character ISO 639 code. If a code is recognized by MediaWiki, use the returned name; if not, then use the value that was provided with the language parameter. When |language= contains a recognized language (either code or name), the page is assigned to the category for that code: Category:Norwegian-language sources (no). For valid three-character code languages, the page is assigned to the single category for '639-2' codes: Category:CS1 ISO 639-2 language sources. Languages that are the same as the local wiki are not categorized. MediaWiki does not recognize three-character equivalents of two-character codes: code 'ar' is recognized but code 'ara' is not. This function supports multiple languages in the form |language=nb, French, th where the language names or codes are separated from each other by commas with optional space characters. ]] local function language_parameter (lang) local tag; -- some form of IETF-like language tag; language subtag with optional region, sript, vatiant, etc subtags local lang_subtag; -- ve populates |language= with mostly unecessary region subtags the MediaWiki does not recognize; this is the base language subtag local name; -- the language name local language_list = {}; -- table of language names to be rendered local names_t = {}; -- table made from the value assigned to |language= local this_wiki_name = mw.language.fetchLanguageName (cfg.this_wiki_code, cfg.this_wiki_code); -- get this wiki's language name names_t = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>; <name> has proper capitalization; <tag> is lowercase if utilities.is_set (tag) then lang_subtag = tag:gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language if 2 == lang_subtag:len() then -- and is a two-character tag utilities.add_prop_cat ('foreign-lang-source', {name, tag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization else -- or is a recognized language (but has a three-character tag) utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template? end elseif cfg.local_lang_cat_enable then -- when the language and this wiki's language are the same and categorization is enabled utilities.add_prop_cat ('local-lang-source', {name, lang_subtag}); -- categorize it end else name = lang; -- return whatever <lang> has so that we show something utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added end table.insert (language_list, name); name = ''; -- so we can reuse it end name = utilities.make_sep_list (#language_list, language_list); if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then -- when only one language, find lang name in this wiki lang name; for |language=en-us, 'English' in 'American English' return ''; -- if one language and that language is this wiki's return an empty string (no annotation) end return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)' --[[ TODO: should only return blank or name rather than full list so we can clean up the bunched parenthetical elements Language, Type, Format ]] end --[[-----------------------< S E T _ C S _ S T Y L E >-------------------------- Gets the default CS style configuration for the given mode. Returns default separator and either postscript as passed in or the default. In CS1, the default postscript and separator are '.'. In CS2, the default postscript is the empty string and the default separator is ','. ]] local function set_cs_style (postscript, mode) if utilities.is_set(postscript) then -- emit a maintenance message if user postscript is the default cs1 postscript -- we catch the opposite case for cs2 in set_style if mode == 'cs1' and postscript == cfg.presentation['ps_' .. mode] then utilities.set_message ('maint_postscript'); end else postscript = cfg.presentation['ps_' .. mode]; end return cfg.presentation['sep_' .. mode], postscript; end --[[--------------------------< S E T _ S T Y L E >----------------------------- Sets the separator and postscript styles. Checks the |mode= first and the #invoke CitationClass second. Removes the postscript if postscript == none. ]] local function set_style (mode, postscript, cite_class) local sep; if 'cs2' == mode then sep, postscript = set_cs_style (postscript, 'cs2'); elseif 'cs1' == mode then sep, postscript = set_cs_style (postscript, 'cs1'); elseif 'citation' == cite_class then sep, postscript = set_cs_style (postscript, 'cs2'); else sep, postscript = set_cs_style (postscript, 'cs1'); end if cfg.keywords_xlate[postscript:lower()] == 'none' then -- emit a maintenance message if user postscript is the default cs2 postscript -- we catch the opposite case for cs1 in set_cs_style if 'cs2' == mode or 'citation' == cite_class then utilities.set_message ('maint_postscript'); end postscript = ''; end return sep, postscript end --[=[-------------------------< I S _ P D F >----------------------------------- Determines if a URL has the file extension that is one of the PDF file extensions used by [[MediaWiki:Common.css]] when applying the PDF icon to external links. returns true if file extension is one of the recognized extensions, else false ]=] local function is_pdf (url) return url:match ('%.pdf$') or url:match ('%.PDF$') or url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or url:match ('%.PDF&#035') or url:match ('%.pdf&#035'); end --[[--------------------------< S T Y L E _ F O R M A T >----------------------- Applies CSS style to |format=, |chapter-format=, etc. Also emits an error message if the format parameter does not have a matching URL parameter. If the format parameter is not set and the URL contains a file extension that is recognized as a PDF document by MediaWiki's commons.css, this code will set the format parameter to (PDF) with the appropriate styling. ]] local function style_format (format, url, fmt_param, url_param) if utilities.is_set (format) then format = utilities.wrap_style ('format', format); -- add leading space, parentheses, resize if not utilities.is_set (url) then utilities.set_message ('err_format_missing_url', {fmt_param, url_param}); -- add an error message end elseif is_pdf (url) then -- format is not set so if URL is a PDF file then format = utilities.wrap_style ('format', 'PDF'); -- set format to PDF else format = ''; -- empty string for concatenation end return format; end --[[---------------------< G E T _ D I S P L A Y _ N A M E S >------------------ Returns a number that defines the number of names displayed for author and editor name lists and a Boolean flag to indicate when et al. should be appended to the name list. When the value assigned to |display-xxxxors= is a number greater than or equal to zero, return the number and the previous state of the 'etal' flag (false by default but may have been set to true if the name list contains some variant of the text 'et al.'). When the value assigned to |display-xxxxors= is the keyword 'etal', return a number that is one greater than the number of authors in the list and set the 'etal' flag true. This will cause the list_people() to display all of the names in the name list followed by 'et al.' In all other cases, returns nil and the previous state of the 'etal' flag. inputs: max: A['DisplayAuthors'] or A['DisplayEditors']; a number or some flavor of etal count: #a or #e list_name: 'authors' or 'editors' etal: author_etal or editor_etal ]] local function get_display_names (max, count, list_name, etal, param) if utilities.is_set (max) then if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings max = count + 1; -- number of authors + 1 so display all author name plus et al. etal = true; -- overrides value set by extract_names() elseif max:match ('^%d+$') then -- if is a string of numbers max = tonumber (max); -- make it a number if max >= count then -- if |display-xxxxors= value greater than or equal to number of authors/editors utilities.set_message ('err_disp_name', {param, max}); -- add error message max = nil; end else -- not a valid keyword or number utilities.set_message ('err_disp_name', {param, max}); -- add error message max = nil; -- unset; as if |display-xxxxors= had not been set end end return max, etal; end --[[----------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >--------------- Adds error if |page=, |pages=, |quote-page=, |quote-pages= has what appears to be some form of p. or pp. abbreviation in the first characters of the parameter content. check page for extraneous p, p., pp, pp., pg, pg. at start of parameter value: good pattern: '^P[^%.P%l]' matches when page begins PX or P# but not Px where x and X are letters and # is a digit bad pattern: '^[Pp][PpGg]' matches when page begins pp, pP, Pp, PP, pg, pG, Pg, PG ]] local function extra_text_in_page_check (val, name) if not val:match (cfg.vol_iss_pg_patterns.good_ppattern) then for _, pattern in ipairs (cfg.vol_iss_pg_patterns.bad_ppatterns) do -- spin through the selected sequence table of patterns if val:match (pattern) then -- when a match, error so utilities.set_message ('err_extra_text_pages', name); -- add error message return; -- and done end end end end --[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------ Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator. For |volume=: 'V.', or 'Vol.' (with or without the dot) abbreviations or 'Volume' in the first characters of the parameter content (all case insensitive). 'V' and 'v' (without the dot) are presumed to be roman numerals so are allowed. For |issue=: 'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the parameter content (all case insensitive). Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or whitespace character) – param values are trimmed of whitespace by MediaWiki before delivered to the module. <val> is |volume= or |issue= parameter value <name> is |volume= or |issue= parameter name for error message <selector> is 'v' for |volume=, 'i' for |issue= sets error message on failure; returns nothing ]] local function extra_text_in_vol_iss_check (val, name, selector) if not utilities.is_set (val) then return; end local patterns = 'v' == selector and cfg.vol_iss_pg_patterns.vpatterns or cfg.vol_iss_pg_patterns.ipatterns; local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue'; val = val:lower(); -- force parameter value to lower case for _, pattern in ipairs (patterns) do -- spin through the selected sequence table of patterns if val:match (pattern) then -- when a match, error so utilities.set_message (handler, name); -- add error message return; -- and done end end end --[=[-------------------------< G E T _ V _ N A M E _ T A B L E >---------------------------------------------- split apart a |vauthors= or |veditors= parameter. This function allows for corporate names, wrapped in doubled parentheses to also have commas; in the old version of the code, the doubled parentheses were included in the rendered citation and in the metadata. Individual author names may be wikilinked |vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.)) ]=] local function get_v_name_table (vparam, output_table, output_link_table) local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas local wl_type, label, link; -- wl_type not used here; just a placeholder local i = 1; while name_table[i] do if name_table[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of corporate with one or more commas; this segment has the opening doubled parentheses local name = name_table[i]; i = i + 1; -- bump indexer to next segment while name_table[i] do name = name .. ', ' .. name_table[i]; -- concatenate with previous segments if name_table[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parentheses break; -- and done reassembling so end i = i + 1; -- bump indexer end table.insert (output_table, name); -- and add corporate name to the output table table.insert (output_link_table, ''); -- no wikilink else wl_type, label, link = utilities.is_wikilink (name_table[i]); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]] table.insert (output_table, label); -- add this name if 1 == wl_type then table.insert (output_link_table, label); -- simple wikilink [[D]] else table.insert (output_link_table, link); -- no wikilink or [[L|D]]; add this link if there is one, else empty string end end i = i + 1; end return output_table; end --[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >-------------------------------- This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and |xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does. Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance tests, are wrapped in doubled parentheses ((corporate name)) to suppress the format tests. Supports generational suffixes Jr, 2nd, 3rd, 4th–6th. This function sets the Vancouver error when a required comma is missing and when there is a space between an author's initials. ]] local function parse_vauthors_veditors (args, vparam, list_name) local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn= local v_name_table = {}; local v_link_table = {}; -- when name is wikilinked, targets go in this table local etal = false; -- return value set to true when we find some form of et al. vauthors parameter local last, first, link, mask, suffix; local corporate = false; vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period) v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas for i, v_name in ipairs(v_name_table) do first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor local accept_name; v_name, accept_name = utilities.has_accept_as_written (v_name); -- remove accept-this-as-written markup when it wraps all of <v_name> if accept_name then last = v_name; corporate = true; -- flag used in list_people() elseif string.find(v_name, "%s") then if v_name:find('[;%.]') then -- look for commonly occurring punctuation characters; add_vanc_error (cfg.err_msg_supl.punctuation, i); end local lastfirstTable = {} lastfirstTable = mw.text.split(v_name, "%s+") first = table.remove(lastfirstTable); -- removes and returns value of last element in table which should be initials or generational suffix if not mw.ustring.match (first, '^%u+$') then -- mw.ustring here so that later we will catch non-Latin characters suffix = first; -- not initials so assume that whatever we got is a generational suffix first = table.remove(lastfirstTable); -- get what should be the initials from the table end last = table.concat(lastfirstTable, ' ') -- returns a string that is the concatenation of all other names that are not initials and generational suffix if not utilities.is_set (last) then first = ''; -- unset last = v_name; -- last empty because something wrong with first add_vanc_error (cfg.err_msg_supl.name, i); end if mw.ustring.match (last, '%a+%s+%u+%s+%a+') then add_vanc_error (cfg.err_msg_supl['missing comma'], i); -- matches last II last; the case when a comma is missing end if mw.ustring.match (v_name, ' %u %u$') then -- this test is in the wrong place TODO: move or replace with a more appropriate test add_vanc_error (cfg.err_msg_supl.initials, i); -- matches a space between two initials end else last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this? end if utilities.is_set (first) then if not mw.ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else add_vanc_error (cfg.err_msg_supl.initials, i); -- too many initials; mixed case initials (which may be ok Romanization); hyphenated initials end is_good_vanc_name (last, first, suffix, i); -- check first and last before restoring the suffix which may have a non-Latin digit if utilities.is_set (suffix) then first = first .. ' ' .. suffix; -- if there was a suffix concatenate with the initials suffix = ''; -- unset so we don't add this suffix to all subsequent names end else if not corporate then is_good_vanc_name (last, '', nil, i); end end link = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ) or v_link_table[i]; mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i ); names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate}; -- add this assembled name to our names list end return names, etal; -- all done, return our list of names end --[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------ Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or select one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list. Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest; |editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn) When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second test which mimicks the test used in extract_names() when looking for a hole in the author name list. There may be a better way to do this, I just haven't discovered what that way is. Emits an error message when more than one xxxxor name source is provided. In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate. ]] local function select_author_editor_source (vxxxxors, xxxxors, args, list_name) local lastfirst = false; if utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 1 ) or -- do this twice in case we have a |first1= without a |last1=; this ... utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 1 ) or -- ... also catches the case where |first= is used with |vauthors= utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 2 ) or utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 2 ) then lastfirst = true; end if (utilities.is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions (utilities.is_set (vxxxxors) and utilities.is_set (xxxxors)) or (true == lastfirst and utilities.is_set (xxxxors)) then local err_name; if 'AuthorList' == list_name then -- figure out which name should be used in error message err_name = 'author'; else err_name = 'editor'; end utilities.set_message ('err_redundant_parameters', err_name .. '-name-list parameters'); -- add error message end if true == lastfirst then return 1 end; -- return a number indicating which author name source to use if utilities.is_set (vxxxxors) then return 2 end; if utilities.is_set (xxxxors) then return 3 end; return 1; -- no authors so return 1; this allows missing author name test to run in case there is a first without last end --[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------ This function is used to validate a parameter's assigned value for those parameters that have only a limited number of allowable values (yes, y, true, live, dead, etc.). When the parameter value has not been assigned a value (missing or empty in the source template) the function returns the value specified by ret_val. If the parameter value is one of the list of allowed values returns the translated value; else, emits an error message and returns the value specified by ret_val. TODO: explain <invert> ]] local function is_valid_parameter_value (value, name, possible, ret_val, invert) if not utilities.is_set (value) then return ret_val; -- an empty parameter is ok end if (not invert and utilities.in_array (value, possible)) then -- normal; <value> is in <possible> table return cfg.keywords_xlate[value]; -- return translation of parameter keyword elseif invert and not utilities.in_array (value, possible) then -- invert; <value> is not in <possible> table return value; -- return <value> as it is else utilities.set_message ('err_invalid_param_val', {name, value}); -- not an allowed value so add error message return ret_val; end end --[[--------------------------< T E R M I N A T E _ N A M E _ L I S T >---------------------------------------- This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a space when the last character is not a sepc character or when the last three characters are not sepc followed by two closing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with a single space character. ]] local function terminate_name_list (name_list, sepc) if (string.sub (name_list, -3, -1) == sepc .. '. ') then -- if already properly terminated return name_list; -- just return the name list elseif (string.sub (name_list, -1, -1) == sepc) or (string.sub (name_list, -3, -1) == sepc .. ']]') then -- if last name in list ends with sepc char return name_list .. " "; -- don't add another else return name_list .. sepc .. ' '; -- otherwise terminate the name list end end --[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >----------------------------------------- returns the concatenation of the formatted volume and issue (or journal article number) parameters as a single string; or formatted volume or formatted issue, or an empty string if neither are set. ]] local function format_volume_issue (volume, issue, article, cite_class, origin, sepc, lower) if not utilities.is_set (volume) and not utilities.is_set (issue) and not utilities.is_set (article) then return ''; end -- same condition as in format_pages_sheets() local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin); local is_numeric_vol = volume and (volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$')); -- is only uppercase roman numerals or only digits? local is_long_vol = volume and (4 < mw.ustring.len(volume)); -- is |volume= value longer than 4 characters? if volume and (not is_numeric_vol and is_long_vol) then -- when not all digits or Roman numerals, is |volume= longer than 4 characters? utilities.add_prop_cat ('long-vol'); -- yes, add properties cat end if is_journal then -- journal-style formatting local vol = ''; if utilities.is_set (volume) then if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals? vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, volume}); -- render in bold face elseif is_long_vol then -- not all digits or Roman numerals; longer than 4 characters? vol = utilities.substitute (cfg.messages['j-vol'], {sepc, utilities.hyphen_to_dash (volume)}); -- not bold else -- four or fewer characters vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, utilities.hyphen_to_dash (volume)}); -- bold end end vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '') vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '') return vol; end if 'podcast' == cite_class and utilities.is_set (issue) then return wrap_msg ('issue', {sepc, issue}, lower); end if 'conference' == cite_class and utilities.is_set (article) then -- |article-number= supported only in journal and conference cites if utilities.is_set (volume) and utilities.is_set (article) then -- both volume and article number return wrap_msg ('vol-art', {sepc, utilities.hyphen_to_dash (volume), article}, lower); elseif utilities.is_set (article) then -- article number alone; when volume alone, handled below return wrap_msg ('art', {sepc, article}, lower); end end -- all other types of citation if utilities.is_set (volume) and utilities.is_set (issue) then return wrap_msg ('vol-no', {sepc, utilities.hyphen_to_dash (volume), issue}, lower); elseif utilities.is_set (volume) then return wrap_msg ('vol', {sepc, utilities.hyphen_to_dash (volume)}, lower); else return wrap_msg ('issue', {sepc, issue}, lower); end end --[[-------------------------< F O R M A T _ P A G E S _ S H E E T S >----------------------------------------- adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings. The return order is: page, pages, sheet, sheets Singular has priority over plural when both are provided. ]] local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower) if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators if utilities.is_set (sheet) then if 'journal' == origin then return '', '', wrap_msg ('j-sheet', sheet, lower), ''; else return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), ''; end elseif utilities.is_set (sheets) then if 'journal' == origin then return '', '', '', wrap_msg ('j-sheets', sheets, lower); else return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower); end end end local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin); if utilities.is_set (page) then if is_journal then return utilities.substitute (cfg.messages['j-page(s)'], page), '', '', ''; elseif not nopp then return utilities.substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', ''; else return utilities.substitute (cfg.messages['nopp'], {sepc, page}), '', '', ''; end elseif utilities.is_set (pages) then if is_journal then return utilities.substitute (cfg.messages['j-page(s)'], pages), '', '', ''; elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number return '', utilities.substitute (cfg.messages['p-prefix'], {sepc, pages}), '', ''; elseif not nopp then return '', utilities.substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', ''; else return '', utilities.substitute (cfg.messages['nopp'], {sepc, pages}), '', ''; end end return '', '', '', ''; -- return empty strings end --[[--------------------------< I N S O U R C E _ L O C _ G E T >---------------------------------------------- returns one of the in-source locators: page, pages, or at. If any of these are interwiki links to Wikisource, returns the label portion of the interwiki-link as plain text for use in COinS. This COinS thing is done because here we convert an interwiki-link to an external link and add an icon span around that; get_coins_pages() doesn't know about the span. TODO: should it? TODO: add support for sheet and sheets?; streamline; TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned to a new name)? ]] local function insource_loc_get (page, page_orig, pages, pages_orig, at) local ws_url, ws_label, coins_pages, L; -- for Wikisource interwiki-links; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?) if utilities.is_set (page) then if utilities.is_set (pages) or utilities.is_set (at) then pages = ''; -- unset the others at = ''; end extra_text_in_page_check (page, page_orig); -- emit error message when |page= value begins with what looks like p., pp., etc. ws_url, ws_label, L = wikisource_url_make (page); -- make ws URL from |page= interwiki link; link portion L becomes tooltip label if ws_url then page = external_link (ws_url, ws_label .. '&nbsp;', 'ws link in page'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? page = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page}); coins_pages = ws_label; end elseif utilities.is_set (pages) then if utilities.is_set (at) then at = ''; -- unset end extra_text_in_page_check (pages, pages_orig); -- emit error message when |page= value begins with what looks like p., pp., etc. ws_url, ws_label, L = wikisource_url_make (pages); -- make ws URL from |pages= interwiki link; link portion L becomes tooltip label if ws_url then pages = external_link (ws_url, ws_label .. '&nbsp;', 'ws link in pages'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? pages = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages}); coins_pages = ws_label; end elseif utilities.is_set (at) then ws_url, ws_label, L = wikisource_url_make (at); -- make ws URL from |at= interwiki link; link portion L becomes tooltip label if ws_url then at = external_link (ws_url, ws_label .. '&nbsp;', 'ws link in at'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? at = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at}); coins_pages = ws_label; end end return page, pages, at, coins_pages; end --[[--------------------------< I S _ U N I Q U E _ A R C H I V E _ U R L >------------------------------------ add error message when |archive-url= value is same as |url= or chapter-url= (or alias...) value ]] local function is_unique_archive_url (archive, url, c_url, source, date) if utilities.is_set (archive) then if archive == url or archive == c_url then utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); -- add error message return '', ''; -- unset |archive-url= and |archive-date= because same as |url= or |chapter-url= end end return archive, date; end --[=[-------------------------< A R C H I V E _ U R L _ C H E C K >-------------------------------------------- Check archive.org URLs to make sure they at least look like they are pointing at valid archives and not to the save snapshot URL or to calendar pages. When the archive URL is 'https://web.archive.org/save/' (or http://...) archive.org saves a snapshot of the target page in the URL. That is something that Wikipedia should not allow unwitting readers to do. When the archive.org URL does not have a complete timestamp, archive.org chooses a snapshot according to its own algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results. This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and |archive-date= and an error message when: |archive-url= holds an archive.org save command URL |archive-url= is an archive.org URL that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the correct place otherwise returns |archive-url= and |archive-date= There are two mostly compatible archive.org URLs: //web.archive.org/<timestamp>... -- the old form //web.archive.org/web/<timestamp>... -- the new form The old form does not support or map to the new form when it contains a display flag. There are four identified flags ('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore) we don't check for these specific flags but we do check the form. This function supports a preview mode. When the article is rendered in preview mode, this function may return a modified archive URL: for save command errors, return undated wildcard (/*/) for timestamp errors when the timestamp has a wildcard, return the URL unmodified for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/) ]=] local function archive_url_check (url, date) local err_msg = ''; -- start with the error message empty local path, timestamp, flag; -- portions of the archive.org URL if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate end if url:match('//web%.archive%.org/save/') then -- if a save command URL, we don't want to allow saving of the target page err_msg = cfg.err_msg_supl.save; url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL elseif url:match('//liveweb%.archive%.org/') then err_msg = cfg.err_msg_supl.liveweb; else path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the URL parts for evaluation if not path then -- malformed in some way; pattern did not match err_msg = cfg.err_msg_supl.timestamp; elseif 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here err_msg = cfg.err_msg_supl.timestamp; if '*' ~= flag then local replacement = timestamp:match ('^%d%d%d%d%d%d') or timestamp:match ('^%d%d%d%d'); -- get the first 6 (YYYYMM) or first 4 digits (YYYY) if replacement then -- nil if there aren't at least 4 digits (year) replacement = replacement .. string.rep ('0', 14 - replacement:len()); -- year or yearmo (4 or 6 digits) zero-fill to make 14-digit timestamp url=url:gsub ('(//web%.archive%.org/[^%d]*)%d[^/]*', '%1' .. replacement .. '*', 1) -- for preview, modify ts to 14 digits plus splat for calendar display end end elseif utilities.is_set (path) and 'web/' ~= path then -- older archive URLs do not have the extra 'web/' path element err_msg = cfg.err_msg_supl.path; elseif utilities.is_set (flag) and not utilities.is_set (path) then -- flag not allowed with the old form URL (without the 'web/' path element) err_msg = cfg.err_msg_supl.flag; elseif utilities.is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element) err_msg = cfg.err_msg_supl.flag; else return url, date; -- return ArchiveURL and ArchiveDate end end -- if here, something not right so utilities.set_message ('err_archive_url', {err_msg}); -- add error message and if is_preview_mode then return url, date; -- preview mode so return ArchiveURL and ArchiveDate else return '', ''; -- return empty strings for ArchiveURL and ArchiveDate end end --[[--------------------------< P L A C E _ C H E C K >-------------------------------------------------------- check |place=, |publication-place=, |location= to see if these params include digits. This function added because many editors misuse location to specify the in-source location (|page(s)= and |at= are supposed to do that) returns the original parameter value without modification; added maint cat when parameter value contains digits ]] local function place_check (param_val) if not utilities.is_set (param_val) then -- parameter empty or omitted return param_val; -- return that empty state end if mw.ustring.find (param_val, '%d') then -- not empty, are there digits in the parameter value utilities.set_message ('maint_location'); -- yep, add maint cat end return param_val; -- and done end --[[--------------------------< I S _ A R C H I V E D _ C O P Y >---------------------------------------------- compares |title= to 'Archived copy' (placeholder added by bots that can't find proper title); if matches, return true; nil else ]] local function is_archived_copy (title) title = mw.ustring.lower(title); -- switch title to lower case if title:find (cfg.special_case_translation.archived_copy.en) then -- if title is 'Archived copy' return true; elseif cfg.special_case_translation.archived_copy['local'] then if mw.ustring.find (title, cfg.special_case_translation.archived_copy['local']) then -- mw.ustring() because might not be Latin script return true; end end end --[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ This is the main function doing the majority of the citation formatting. ]] local function citation0( config, args ) --[[ Load Input Parameters The argument_wrapper facilitates the mapping of multiple aliases to single internal variable. ]] local A = argument_wrapper ( args ); local i -- Pick out the relevant fields from the arguments. Different citation templates -- define different field names for the same underlying things. local author_etal; local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors= local Authors; local NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], ''); local Collaboration = A['Collaboration']; do -- to limit scope of selected local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList'); if 1 == selected then a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn= elseif 2 == selected then NameListStyle = 'vanc'; -- override whatever |name-list-style= might be a, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn= elseif 3 == selected then Authors = A['Authors']; -- use content of |authors= if 'authors' == A:ORIGIN('Authors') then -- but add a maint cat if the parameter is |authors= utilities.set_message ('maint_authors'); -- because use of this parameter is discouraged; what to do about the aliases is a TODO: end end if utilities.is_set (Collaboration) then author_etal = true; -- so that |display-authors=etal not required end end local editor_etal; local e = {}; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors= do -- to limit scope of selected local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList'); -- support for |editors= withdrawn if 1 == selected then e, editor_etal = extract_names (args, 'EditorList'); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn= elseif 2 == selected then NameListStyle = 'vanc'; -- override whatever |name-list-style= might be e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn= end end local Chapter = A['Chapter']; -- done here so that we have access to |contribution= from |chapter= aliases local Chapter_origin = A:ORIGIN ('Chapter'); local Contribution; -- because contribution is required for contributor(s) if 'contribution' == Chapter_origin then Contribution = Chapter; -- get the name of the contribution end local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (A['Periodical']) then -- |contributor= and |contribution= only supported in book cites c = extract_names (args, 'ContributorList'); -- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn= if 0 < #c then if not utilities.is_set (Contribution) then -- |contributor= requires |contribution= utilities.set_message ('err_contributor_missing_required_param', 'contribution'); -- add missing contribution error message c = {}; -- blank the contributors' table; it is used as a flag later end if 0 == #a then -- |contributor= requires |author= utilities.set_message ('err_contributor_missing_required_param', 'author'); -- add missing author error message c = {}; -- blank the contributors' table; it is used as a flag later end end else -- if not a book cite if utilities.select_one (args, cfg.aliases['ContributorList-Last'], 'err_redundant_parameters', 1 ) then -- are there contributor name list parameters? utilities.set_message ('err_contributor_ignored'); -- add contributor ignored error message end Contribution = nil; -- unset end local Title = A['Title']; local TitleLink = A['TitleLink']; local auto_select = ''; -- default is auto local accept_link; TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup if (not accept_link) and utilities.in_array (TitleLink, {'none', 'pmc', 'doi'}) then -- check for special keywords auto_select = TitleLink; -- remember selection for later TitleLink = ''; -- treat as if |title-link= would have been empty end TitleLink = link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title'); -- check for wiki-markup in |title-link= or wiki-markup in |title= when |title-link= is set local Section = ''; -- {{cite map}} only; preset to empty string for concatenation if not used if 'map' == config.CitationClass and 'section' == Chapter_origin then Section = A['Chapter']; -- get |section= from |chapter= alias list; |chapter= and the other aliases not supported in {{cite map}} Chapter = ''; -- unset for now; will be reset later from |map= if present end local Periodical = A['Periodical']; local Periodical_origin = ''; if utilities.is_set (Periodical) then Periodical_origin = A:ORIGIN('Periodical'); -- get the name of the periodical parameter local i; Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated if i then -- non-zero when markup was stripped so emit an error message utilities.set_message ('err_apostrophe_markup', {Periodical_origin}); end end if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}} if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'mailinglist')}); end Periodical = A ['MailingList']; -- error or no, set Periodical to |mailinglist= value because this template is {{cite mailing list}} Periodical_origin = A:ORIGIN('MailingList'); end local ScriptPeriodical = A['ScriptPeriodical']; -- web and news not tested for now because of -- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_"Cite_Web"_errors? if not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) then -- 'periodical' templates require periodical parameter -- local p = {['journal'] = 'journal', ['magazine'] = 'magazine', ['news'] = 'newspaper', ['web'] = 'website'}; -- for error message local p = {['journal'] = 'journal', ['magazine'] = 'magazine'}; -- for error message if p[config.CitationClass] then utilities.set_message ('err_missing_periodical', {config.CitationClass, p[config.CitationClass]}); end end local Volume; local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical'); if 'citation' == config.CitationClass then if utilities.is_set (Periodical) then if not utilities.in_array (Periodical_origin, cfg.citation_no_volume_t) then -- {{citation}} does not render |volume= when these parameters are used Volume = A['Volume']; -- but does for all other 'periodicals' end elseif utilities.is_set (ScriptPeriodical) then if 'script-website' ~= ScriptPeriodical_origin then -- {{citation}} does not render volume for |script-website= Volume = A['Volume']; -- but does for all other 'periodicals' end else Volume = A['Volume']; -- and does for non-'periodical' cites end elseif utilities.in_array (config.CitationClass, cfg.templates_using_volume) then -- render |volume= for cs1 according to the configuration settings Volume = A['Volume']; end extra_text_in_vol_iss_check (Volume, A:ORIGIN ('Volume'), 'v'); local Issue; if 'citation' == config.CitationClass then if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, cfg.citation_issue_t) then -- {{citation}} may render |issue= when these parameters are used Issue = utilities.hyphen_to_dash (A['Issue']); end elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table if not (utilities.in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical))) then Issue = utilities.hyphen_to_dash (A['Issue']); end end local ArticleNumber; if utilities.in_array (config.CitationClass, {'journal', 'conference'}) or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then ArticleNumber = A['ArticleNumber']; end extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i'); local Page; local Pages; local At; local QuotePage; local QuotePages; if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then -- TODO: rewrite to emit ignored parameter error message? Page = A['Page']; Pages = utilities.hyphen_to_dash (A['Pages']); At = A['At']; QuotePage = A['QuotePage']; QuotePages = utilities.hyphen_to_dash (A['QuotePages']); end local Edition = A['Edition']; local PublicationPlace = place_check (A['PublicationPlace'], A:ORIGIN('PublicationPlace')); local Place = place_check (A['Place'], A:ORIGIN('Place')); local PublisherName = A['PublisherName']; local PublisherName_origin = A:ORIGIN('PublisherName'); if utilities.is_set (PublisherName) then local i = 0; PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized if i then -- non-zero when markup was stripped so emit an error message utilities.set_message ('err_apostrophe_markup', {PublisherName_origin}); end end local Newsgroup = A['Newsgroup']; -- TODO: strip apostrophe markup? local Newsgroup_origin = A:ORIGIN('Newsgroup'); if 'newsgroup' == config.CitationClass then if utilities.is_set (PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup utilities.set_message ('err_parameter_ignored', {PublisherName_origin}); end PublisherName = nil; -- ensure that this parameter is unset for the time being; will be used again after COinS end local URL = A['URL']; -- TODO: better way to do this for URL, ChapterURL, and MapURL? local UrlAccess = is_valid_parameter_value (A['UrlAccess'], A:ORIGIN('UrlAccess'), cfg.keywords_lists['url-access'], nil); if not utilities.is_set (URL) and utilities.is_set (UrlAccess) then UrlAccess = nil; utilities.set_message ('err_param_access_requires_param', 'url'); end local ChapterURL = A['ChapterURL']; local ChapterUrlAccess = is_valid_parameter_value (A['ChapterUrlAccess'], A:ORIGIN('ChapterUrlAccess'), cfg.keywords_lists['url-access'], nil); if not utilities.is_set (ChapterURL) and utilities.is_set (ChapterUrlAccess) then ChapterUrlAccess = nil; utilities.set_message ('err_param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')}); end local MapUrlAccess = is_valid_parameter_value (A['MapUrlAccess'], A:ORIGIN('MapUrlAccess'), cfg.keywords_lists['url-access'], nil); if not utilities.is_set (A['MapURL']) and utilities.is_set (MapUrlAccess) then MapUrlAccess = nil; utilities.set_message ('err_param_access_requires_param', {'map-url'}); end local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language local no_tracking_cats = is_valid_parameter_value (A['NoTracking'], A:ORIGIN('NoTracking'), cfg.keywords_lists['yes_true_y'], nil); -- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories if not utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids? no_tracking_cats = "true"; -- set no_tracking_cats end for _, v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns if this_page.text:match (v) then -- test page name against each pattern no_tracking_cats = "true"; -- set no_tracking_cats break; -- bail out if one is found end end end -- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it) utilities.select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'err_redundant_parameters'); -- this is a dummy call simply to get the error message and category local coins_pages; Page, Pages, At, coins_pages = insource_loc_get (Page, A:ORIGIN('Page'), Pages, A:ORIGIN('Pages'), At); local NoPP = is_valid_parameter_value (A['NoPP'], A:ORIGIN('NoPP'), cfg.keywords_lists['yes_true_y'], nil); if utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- both |publication-place= and |place= (|location=) allowed if different utilities.add_prop_cat ('location-test'); -- add property cat to evaluate how often PublicationPlace and Place are used together if PublicationPlace == Place then Place = ''; -- unset; don't need both if they are the same end elseif not utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- when only |place= (|location=) is set ... PublicationPlace = Place; -- promote |place= (|location=) to |publication-place end if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL local ScriptChapter = A['ScriptChapter']; local ScriptChapter_origin = A:ORIGIN ('ScriptChapter'); local Format = A['Format']; local ChapterFormat = A['ChapterFormat']; local TransChapter = A['TransChapter']; local TransChapter_origin = A:ORIGIN ('TransChapter'); local TransTitle = A['TransTitle']; local ScriptTitle = A['ScriptTitle']; --[[ Parameter remapping for cite encyclopedia: When the citation has these parameters: |encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title= |encyclopedia= and |article= then map |encyclopedia= to |title= |trans-title= maps to |trans-chapter= when |title= is re-mapped |url= maps to |chapter-url= when |title= is remapped All other combinations of |encyclopedia=, |title=, and |article= are not modified ]] local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS if utilities.is_set (Encyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}} if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')}); Encyclopedia = nil; -- unset because not supported by this template end end if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both set emit an error TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', A:ORIGIN ('Encyclopedia')) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Periodical_origin)}); end if utilities.is_set (Encyclopedia) then Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia; allow periodical without encyclopedia Periodical_origin = A:ORIGIN ('Encyclopedia'); end if utilities.is_set (Periodical) then -- Periodical is set when |encyclopedia= is set if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then if not utilities.is_set (Chapter) then Chapter = Title; -- |encyclopedia= and |title= are set so map |title= to |article= and |encyclopedia= to |title= ScriptChapter = ScriptTitle; ScriptChapter_origin = A:ORIGIN('ScriptTitle') TransChapter = TransTitle; ChapterURL = URL; ChapterURL_origin = URL_origin; ChapterUrlAccess = UrlAccess; if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then Chapter = utilities.make_wikilink (TitleLink, Chapter); end Title = Periodical; ChapterFormat = Format; Periodical = ''; -- redundant so unset TransTitle = ''; URL = ''; Format = ''; TitleLink = ''; ScriptTitle = ''; end elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title= Periodical = ''; -- redundant so unset end end end -- special case for cite techreport. local ID = A['ID']; if (config.CitationClass == "techreport") then -- special case for cite techreport if utilities.is_set (A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue' if not utilities.is_set (ID) then -- can we use ID for the "number"? ID = A['Number']; -- yes, use it else -- ID has a value so emit error message utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'number')}); end end end -- Account for the oddity that is {{cite conference}}, before generation of COinS data. local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode local Conference = A['Conference']; local BookTitle = A['BookTitle']; local TransTitle_origin = A:ORIGIN ('TransTitle'); if 'conference' == config.CitationClass then if utilities.is_set (BookTitle) then Chapter = Title; Chapter_origin = 'title'; -- ChapterLink = TitleLink; -- |chapter-link= is deprecated ChapterURL = URL; ChapterUrlAccess = UrlAccess; ChapterURL_origin = URL_origin; URL_origin = ''; ChapterFormat = Format; TransChapter = TransTitle; TransChapter_origin = TransTitle_origin; Title = BookTitle; Format = ''; -- TitleLink = ''; TransTitle = ''; URL = ''; end elseif 'speech' ~= config.CitationClass then Conference = ''; -- not cite conference or cite speech so make sure this is empty string end -- CS1/2 mode local Mode = is_valid_parameter_value (A['Mode'], A:ORIGIN('Mode'), cfg.keywords_lists['mode'], ''); -- separator character and postscript local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass); -- controls capitalization of certain static text local use_lowercase = ( sepc == ',' ); -- cite map oddities local Cartography = ""; local Scale = ""; local Sheet = A['Sheet'] or ''; local Sheets = A['Sheets'] or ''; if config.CitationClass == "map" then if utilities.is_set (Chapter) then --TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message end Chapter = A['Map']; Chapter_origin = A:ORIGIN('Map'); ChapterURL = A['MapURL']; ChapterURL_origin = A:ORIGIN('MapURL'); TransChapter = A['TransMap']; ScriptChapter = A['ScriptMap'] ScriptChapter_origin = A:ORIGIN('ScriptMap') ChapterUrlAccess = MapUrlAccess; ChapterFormat = A['MapFormat']; Cartography = A['Cartography']; if utilities.is_set ( Cartography ) then Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase); end Scale = A['Scale']; if utilities.is_set ( Scale ) then Scale = sepc .. " " .. Scale; end end -- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data. local Series = A['Series']; if 'episode' == config.CitationClass or 'serial' == config.CitationClass then local SeriesLink = A['SeriesLink']; SeriesLink = link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series'); -- check for wiki-markup in |series-link= or wiki-markup in |series= when |series-link= is set local Network = A['Network']; local Station = A['Station']; local s, n = {}, {}; -- do common parameters first if utilities.is_set (Network) then table.insert(n, Network); end if utilities.is_set (Station) then table.insert(n, Station); end ID = table.concat(n, sepc .. ' '); if 'episode' == config.CitationClass then -- handle the oddities that are strictly {{cite episode}} local Season = A['Season']; local SeriesNumber = A['SeriesNumber']; if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message SeriesNumber = ''; -- unset; prefer |season= over |seriesno= end -- assemble a table of parts concatenated later into Series if utilities.is_set (Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end if utilities.is_set (SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end if utilities.is_set (Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end Issue = ''; -- unset because this is not a unique parameter Chapter = Title; -- promote title parameters to chapter ScriptChapter = ScriptTitle; ScriptChapter_origin = A:ORIGIN('ScriptTitle'); ChapterLink = TitleLink; -- alias |episode-link= TransChapter = TransTitle; ChapterURL = URL; ChapterUrlAccess = UrlAccess; ChapterURL_origin = URL_origin; ChapterFormat = Format; Title = Series; -- promote series to title TitleLink = SeriesLink; Series = table.concat(s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number if utilities.is_set (ChapterLink) and not utilities.is_set (ChapterURL) then -- link but not URL Chapter = utilities.make_wikilink (ChapterLink, Chapter); elseif utilities.is_set (ChapterLink) and utilities.is_set (ChapterURL) then -- if both are set, URL links episode; Series = utilities.make_wikilink (ChapterLink, Series); end URL = ''; -- unset TransTitle = ''; ScriptTitle = ''; Format = ''; else -- now oddities that are cite serial Issue = ''; -- unset because this parameter no longer supported by the citation/core version of cite serial Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday? if utilities.is_set (Series) and utilities.is_set (SeriesLink) then Series = utilities.make_wikilink (SeriesLink, Series); end Series = utilities.wrap_style ('italic-title', Series); -- series is italicized end end -- end of {{cite episode}} stuff -- handle type parameter for those CS1 citations that have default values local TitleType = A['TitleType']; local Degree = A['Degree']; if utilities.in_array (config.CitationClass, {'AV-media-notes', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then TitleType = set_titletype (config.CitationClass, TitleType); if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower(); end end if utilities.is_set (TitleType) then -- if type parameter is specified TitleType = utilities.substitute ( cfg.messages['type'], TitleType); -- display it in parentheses -- TODO: Hack on TitleType to fix bunched parentheses problem end -- legacy: promote PublicationDate to Date if neither Date nor Year are set. local Date = A['Date']; local Date_origin; -- to hold the name of parameter promoted to Date; required for date error messaging local PublicationDate = A['PublicationDate']; local Year = A['Year']; if not utilities.is_set (Date) then Date = Year; -- promote Year to Date Year = nil; -- make nil so Year as empty string isn't used for CITEREF if not utilities.is_set (Date) and utilities.is_set (PublicationDate) then -- use PublicationDate when |date= and |year= are not set Date = PublicationDate; -- promote PublicationDate to Date PublicationDate = ''; -- unset, no longer needed Date_origin = A:ORIGIN('PublicationDate'); -- save the name of the promoted parameter else Date_origin = A:ORIGIN('Year'); -- save the name of the promoted parameter end else Date_origin = A:ORIGIN('Date'); -- not a promotion; name required for error messaging end if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation --[[ Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where we get the date used in the metadata. Date validation supporting code is in Module:Citation/CS1/Date_validation ]] local DF = is_valid_parameter_value (A['DF'], A:ORIGIN('DF'), cfg.keywords_lists['df'], ''); if not utilities.is_set (DF) then DF = cfg.global_df; -- local |df= if present overrides global df set by {{use xxx date}} template end local ArchiveURL; local ArchiveDate; local ArchiveFormat = A['ArchiveFormat']; ArchiveURL, ArchiveDate = archive_url_check (A['ArchiveURL'], A['ArchiveDate']) ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url'); ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL local AccessDate = A['AccessDate']; local LayDate = A['LayDate']; local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification local DoiBroken = A['DoiBroken']; local Embargo = A['Embargo']; local anchor_year; -- used in the CITEREF identifier do -- create defined block to contain local variables error_message, date_parameters_list, mismatch local error_message = ''; -- AirDate has been promoted to Date so not necessary to check it local date_parameters_list = { ['access-date'] = {val = AccessDate, name = A:ORIGIN ('AccessDate')}, ['archive-date'] = {val = ArchiveDate, name = A:ORIGIN ('ArchiveDate')}, ['date'] = {val = Date, name = Date_origin}, ['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')}, ['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')}, ['lay-date'] = {val = LayDate, name = A:ORIGIN ('LayDate')}, ['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')}, ['year'] = {val = Year, name = A:ORIGIN ('Year')}, }; local error_list = {}; anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list); -- start temporary Julian / Gregorian calendar uncertainty categorization if COinS_date.inter_cal_cat then utilities.add_prop_cat ('jul-greg-uncertainty'); end -- end temporary Julian / Gregorian calendar uncertainty categorization if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed; validation.year_date_check (Year, A:ORIGIN ('Year'), Date, A:ORIGIN ('Date'), error_list); end if 0 == #error_list then -- error free dates only; 0 when error_list is empty local modified = false; -- flag if utilities.is_set (DF) then -- if we need to reformat dates modified = validation.reformat_dates (date_parameters_list, DF); -- reformat to DF format, use long month names if appropriate end if true == validation.date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate modified = true; utilities.set_message ('maint_date_format'); -- hyphens were converted so add maint category end -- for those wikis that can and want to have English date names translated to the local language; not supported at en.wiki if cfg.date_name_auto_xlate_enable and validation.date_name_xlate (date_parameters_list, cfg.date_digit_auto_xlate_enable ) then utilities.set_message ('maint_date_auto_xlated'); -- add maint cat modified = true; end if modified then -- if the date_parameters_list values were modified AccessDate = date_parameters_list['access-date'].val; -- overwrite date holding parameters with modified values ArchiveDate = date_parameters_list['archive-date'].val; Date = date_parameters_list['date'].val; DoiBroken = date_parameters_list['doi-broken-date'].val; LayDate = date_parameters_list['lay-date'].val; PublicationDate = date_parameters_list['publication-date'].val; end else utilities.set_message ('err_bad_date', {utilities.make_sep_list (#error_list, error_list)}); -- add this error message end end -- end of do local ID_list = {}; -- sequence table of rendered identifiers local ID_list_coins = {}; -- table of identifiers and their values from args; key is same as cfg.id_handlers's key local Class = A['Class']; -- arxiv class identifier local ID_support = { {A['ASINTLD'], 'ASIN', 'err_asintld_missing_asin', A:ORIGIN ('ASINTLD')}, {DoiBroken, 'DOI', 'err_doibroken_missing_doi', A:ORIGIN ('DoiBroken')}, {Embargo, 'PMC', 'err_embargo_missing_pmc', A:ORIGIN ('Embargo')}, } ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class}, ID_support); -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite ssrn}}, before generation of COinS data. if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv=, |citeseerx=, |ssrn= required for their templates if not (args[cfg.id_handlers[config.CitationClass:upper()].parameters[1]] or -- can't use ID_list_coins k/v table here because invalid parameters omitted args[cfg.id_handlers[config.CitationClass:upper()].parameters[2]]) then -- which causes unexpected parameter missing error message utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message end Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass]; end -- Link the title of the work if no |url= was provided, but we have a |pmc= or a |doi= with |doi-access=free if config.CitationClass == "journal" and not utilities.is_set (URL) and not utilities.is_set (TitleLink) and not utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) then -- TODO: remove 'none' once existing citations have been switched to 'off', so 'none' can be used as token for "no title" instead if 'none' ~= cfg.keywords_xlate[auto_select] then -- if auto-linking not disabled if identifiers.auto_link_urls[auto_select] then -- manual selection URL = identifiers.auto_link_urls[auto_select]; -- set URL to be the same as identifier's external link URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title= elseif identifiers.auto_link_urls['pmc'] then -- auto-select PMC URL = identifiers.auto_link_urls['pmc']; -- set URL to be the same as the PMC external link if not embargoed URL_origin = cfg.id_handlers['PMC'].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title= elseif identifiers.auto_link_urls['doi'] then -- auto-select DOI URL = identifiers.auto_link_urls['doi']; URL_origin = cfg.id_handlers['DOI'].parameters[1]; end end if utilities.is_set (URL) then -- set when using an identifier-created URL if utilities.is_set (AccessDate) then -- |access-date= requires |url=; identifier-created URL is not |url= utilities.set_message ('err_accessdate_missing_url'); -- add an error message AccessDate = ''; -- unset end if utilities.is_set (ArchiveURL) then -- |archive-url= requires |url=; identifier-created URL is not |url= utilities.set_message ('err_archive_missing_url'); -- add an error message ArchiveURL = ''; -- unset end end end -- At this point fields may be nil if they weren't specified in the template use. We can use that fact. -- Test if citation has no title if not utilities.is_set (Title) and not utilities.is_set (TransTitle) and not utilities.is_set (ScriptTitle) then -- has special case for cite episode utilities.set_message ('err_citation_missing_title', {'episode' == config.CitationClass and 'series' or 'title'}); end if utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) and utilities.in_array (config.CitationClass, {'journal', 'citation'}) and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and ('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then -- special case for journal cites Title = ''; -- set title to empty string utilities.set_message ('maint_untitled'); -- add maint cat end -- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information. -- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that -- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title -- is the article title, and Chapter is a section within the article. So, we remap local coins_chapter = Chapter; -- default assuming that remapping not required local coins_title = Title; -- et tu if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then if utilities.is_set (Chapter) and utilities.is_set (Title) and utilities.is_set (Periodical) then -- if all are used then coins_chapter = Title; -- remap coins_title = Periodical; end end local coins_author = a; -- default for coins rft.au if 0 < #c then -- but if contributor list coins_author = c; -- use that instead end -- this is the function call to COinS() local OCinSoutput = metadata.COinS({ ['Periodical'] = utilities.strip_apostrophe_markup (Periodical), -- no markup in the metadata ['Encyclopedia'] = Encyclopedia, -- just a flag; content ignored by ~/COinS ['Chapter'] = metadata.make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic / accept-as-written markup ['Degree'] = Degree; -- cite thesis only ['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup ['PublicationPlace'] = PublicationPlace, ['Date'] = COinS_date.rftdate, -- COinS_date has correctly formatted date if Date is valid; ['Season'] = COinS_date.rftssn, ['Quarter'] = COinS_date.rftquarter, ['Chron'] = COinS_date.rftchron or (not COinS_date.rftdate and Date) or '', -- chron but if not set and invalid date format use Date; keep this last bit? ['Series'] = Series, ['Volume'] = Volume, ['Issue'] = Issue, ['ArticleNumber'] = ArticleNumber, ['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links ['Edition'] = Edition, ['PublisherName'] = PublisherName or Newsgroup, -- any apostrophe markup already removed from PublisherName ['URL'] = first_set ({ChapterURL, URL}, 2), ['Authors'] = coins_author, ['ID_list'] = ID_list_coins, ['RawPage'] = this_page.prefixedText, }, config.CitationClass); -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, and {{cite ssrn}} AFTER generation of COinS data. if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, or ssrn now unset so it isn't displayed Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal end -- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text if 'newsgroup' == config.CitationClass and utilities.is_set (Newsgroup) then PublisherName = utilities.substitute (cfg.messages['newsgroup'], external_link( 'news:' .. Newsgroup, Newsgroup, Newsgroup_origin, nil )); end local Editors; local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list local Contributors; -- assembled contributors name list local contributor_etal; local Translators; -- assembled translators name list local translator_etal; local t = {}; -- translators list from |translator-lastn= / translator-firstn= pairs t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn= local Interviewers; local interviewers_list = {}; interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters local interviewer_etal; -- Now perform various field substitutions. -- We also add leading spaces and surrounding markup and punctuation to the -- various parts of the citation, but only when they are non-nil. do local last_first_list; local control = { format = NameListStyle, -- empty string or 'vanc' maximum = nil, -- as if display-authors or display-editors not set mode = Mode }; do -- do editor name list first because the now unsupported coauthors used to modify control table control.maximum , editor_etal = get_display_names (A['DisplayEditors'], #e, 'editors', editor_etal, A:ORIGIN ('DisplayEditors')); Editors, EditorCount = list_people (control, e, editor_etal); if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then EditorCount = 2; -- spoof to display (eds.) annotation end end do -- now do interviewers control.maximum, interviewer_etal = get_display_names (A['DisplayInterviewers'], #interviewers_list, 'interviewers', interviewer_etal, A:ORIGIN ('DisplayInterviewers')); Interviewers = list_people (control, interviewers_list, interviewer_etal); end do -- now do translators control.maximum, translator_etal = get_display_names (A['DisplayTranslators'], #t, 'translators', translator_etal, A:ORIGIN ('DisplayTranslators')); Translators = list_people (control, t, translator_etal); end do -- now do contributors control.maximum, contributor_etal = get_display_names (A['DisplayContributors'], #c, 'contributors', contributor_etal, A:ORIGIN ('DisplayContributors')); Contributors = list_people (control, c, contributor_etal); end do -- now do authors control.maximum, author_etal = get_display_names (A['DisplayAuthors'], #a, 'authors', author_etal, A:ORIGIN ('DisplayAuthors')); last_first_list = list_people (control, a, author_etal); if utilities.is_set (Authors) then Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors'); -- find and remove variations on et al. if author_etal then Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter end else Authors = last_first_list; -- either an author name list or an empty string end end -- end of do if utilities.is_set (Authors) and utilities.is_set (Collaboration) then Authors = Authors .. ' (' .. Collaboration .. ')'; -- add collaboration after et al. end end local ConferenceFormat = A['ConferenceFormat']; local ConferenceURL = A['ConferenceURL']; ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url'); Format = style_format (Format, URL, 'format', 'url'); -- special case for chapter format so no error message or cat when chapter not supported if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or ('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url'); end if not utilities.is_set (URL) then if utilities.in_array (config.CitationClass, {"web", "podcast", "mailinglist"}) or -- |url= required for cite web, cite podcast, and cite mailinglist ('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then -- and required for {{citation}} with |website= or |script-website= utilities.set_message ('err_cite_web_url'); end -- do we have |accessdate= without either |url= or |chapter-url=? if utilities.is_set (AccessDate) and not utilities.is_set (ChapterURL) then -- ChapterURL may be set when URL is not set; utilities.set_message ('err_accessdate_missing_url'); AccessDate = ''; end end local UrlStatus = is_valid_parameter_value (A['UrlStatus'], A:ORIGIN('UrlStatus'), cfg.keywords_lists['url-status'], ''); local OriginalURL local OriginalURL_origin local OriginalFormat local OriginalAccess; UrlStatus = UrlStatus:lower(); -- used later when assembling archived text if utilities.is_set ( ArchiveURL ) then if utilities.is_set (ChapterURL) then -- if chapter-url= is set apply archive url to it OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text OriginalURL_origin = ChapterURL_origin; -- name of |chapter-url= parameter for error messages OriginalFormat = ChapterFormat; -- and original |chapter-format= if 'live' ~= UrlStatus then ChapterURL = ArchiveURL -- swap-in the archive's URL ChapterURL_origin = A:ORIGIN('ArchiveURL') -- name of |archive-url= parameter for error messages ChapterFormat = ArchiveFormat or ''; -- swap in archive's format ChapterUrlAccess = nil; -- restricted access levels do not make sense for archived URLs end elseif utilities.is_set (URL) then OriginalURL = URL; -- save copy of original source URL OriginalURL_origin = URL_origin; -- name of URL parameter for error messages OriginalFormat = Format; -- and original |format= OriginalAccess = UrlAccess; if 'live' ~= UrlStatus then -- if URL set then |archive-url= applies to it URL = ArchiveURL -- swap-in the archive's URL URL_origin = A:ORIGIN('ArchiveURL') -- name of archive URL parameter for error messages Format = ArchiveFormat or ''; -- swap in archive's format UrlAccess = nil; -- restricted access levels do not make sense for archived URLs end end elseif utilities.is_set (UrlStatus) then -- if |url-status= is set when |archive-url= is not set utilities.set_message ('maint_url_status'); -- add maint cat end if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia ('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then local chap_param; if utilities.is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters chap_param = A:ORIGIN ('Chapter') elseif utilities.is_set (TransChapter) then chap_param = A:ORIGIN ('TransChapter') elseif utilities.is_set (ChapterURL) then chap_param = A:ORIGIN ('ChapterURL') elseif utilities.is_set (ScriptChapter) then chap_param = ScriptChapter_origin; else utilities.is_set (ChapterFormat) chap_param = A:ORIGIN ('ChapterFormat') end if utilities.is_set (chap_param) then -- if we found one utilities.set_message ('err_chapter_ignored', {chap_param}); -- add error message Chapter = ''; -- and set them to empty string to be safe with concatenation TransChapter = ''; ChapterURL = ''; ScriptChapter = ''; ChapterFormat = ''; end else -- otherwise, format chapter / article title local no_quotes = false; -- default assume that we will be quoting the chapter parameter value if utilities.is_set (Contribution) and 0 < #c then -- if this is a contribution with contributor(s) if utilities.in_array (Contribution:lower(), cfg.keywords_lists.contribution) then -- and a generic contribution title no_quotes = true; -- then render it unquoted end end Chapter = format_chapter_title (ScriptChapter, ScriptChapter_origin, Chapter, Chapter_origin, TransChapter, TransChapter_origin, ChapterURL, ChapterURL_origin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter if utilities.is_set (Chapter) then Chapter = Chapter .. ChapterFormat ; if 'map' == config.CitationClass and utilities.is_set (TitleType) then Chapter = Chapter .. ' ' .. TitleType; -- map annotation here; not after title end Chapter = Chapter .. sepc .. ' '; elseif utilities.is_set (ChapterFormat) then -- |chapter= not set but |chapter-format= is so ... Chapter = ChapterFormat .. sepc .. ' '; -- ... ChapterFormat has error message, we want to see it end end -- Format main title local plain_title = false; local accept_title; Title, accept_title = utilities.has_accept_as_written (Title, true); -- remove accept-this-as-written markup when it wraps all of <Title> if accept_title and ('' == Title) then -- only support forced empty for now "(())" Title = cfg.messages['notitle']; -- replace by predefined "No title" message -- TODO: utilities.set_message ( 'err_redundant_parameters', ...); -- issue proper error message instead of muting ScriptTitle = ''; -- just mute for now TransTitle = ''; -- just mute for now plain_title = true; -- suppress text decoration for descriptive title utilities.set_message ('maint_untitled'); -- add maint cat end if not accept_title then -- <Title> not wrapped in accept-as-written markup if '...' == Title:sub (-3) then -- if ellipsis is the last three characters of |title= Title = Title:gsub ('(%.%.%.)%.+$', '%1'); -- limit the number of dots to three elseif not mw.ustring.find (Title, '%.%s*%a%.$') and -- end of title is not a 'dot-(optional space-)letter-dot' initialism ... not mw.ustring.find (Title, '%s+%a%.$') then -- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.) Title = mw.ustring.gsub(Title, '%' .. sepc .. '$', ''); -- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters end if utilities.is_set (ArchiveURL) and is_archived_copy (Title) then utilities.set_message ('maint_archived_copy'); -- add maintenance category before we modify the content of Title end if is_generic ('generic_titles', Title) then utilities.set_message ('err_generic_title'); -- set an error message end end if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or ('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or ('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from module provided quote marks Title = utilities.wrap_style ('quoted-title', Title); Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); elseif plain_title or ('report' == config.CitationClass) then -- no styling for cite report and descriptive titles (otherwise same as above) Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title else Title = utilities.wrap_style ('italic-title', Title); Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped TransTitle = utilities.wrap_style ('trans-italic-title', TransTitle); end if utilities.is_set (TransTitle) then if utilities.is_set (Title) then TransTitle = " " .. TransTitle; else utilities.set_message ('err_trans_missing_title', {'title'}); end end if utilities.is_set (Title) then -- TODO: is this the right place to be making Wikisource URLs? if utilities.is_set (TitleLink) and utilities.is_set (URL) then utilities.set_message ('err_wikilink_in_url'); -- set an error message because we can't have both TitleLink = ''; -- unset end if not utilities.is_set (TitleLink) and utilities.is_set (URL) then Title = external_link (URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format; URL = ''; -- unset these because no longer needed Format = ""; elseif utilities.is_set (TitleLink) and not utilities.is_set (URL) then local ws_url; ws_url = wikisource_url_make (TitleLink); -- ignore ws_label return; not used here if ws_url then Title = external_link (ws_url, Title .. '&nbsp;', 'ws link in title-link'); -- space char after Title to move icon away from italic text; TODO: a better way to do this? Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title}); Title = Title .. TransTitle; else Title = utilities.make_wikilink (TitleLink, Title) .. TransTitle; end else local ws_url, ws_label, L; -- Title has italic or quote markup by the time we get here which causes is_wikilink() to return 0 (not a wikilink) ws_url, ws_label, L = wikisource_url_make (Title:gsub('^[\'"]*(.-)[\'"]*$', '%1')); -- make ws URL from |title= interwiki link (strip italic or quote markup); link portion L becomes tooltip label if ws_url then Title = Title:gsub ('%b[]', ws_label); -- replace interwiki link with ws_label to retain markup Title = external_link (ws_url, Title .. '&nbsp;', 'ws link in title'); -- space char after Title to move icon away from italic text; TODO: a better way to do this? Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title}); Title = Title .. TransTitle; else Title = Title .. TransTitle; end end else Title = TransTitle; end if utilities.is_set (Place) then Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " "; end local ConferenceURL_origin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL if utilities.is_set (Conference) then if utilities.is_set (ConferenceURL) then Conference = external_link( ConferenceURL, Conference, ConferenceURL_origin, nil ); end Conference = sepc .. " " .. Conference .. ConferenceFormat; elseif utilities.is_set (ConferenceURL) then Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURL_origin, nil ); end local Position = ''; if not utilities.is_set (Position) then local Minutes = A['Minutes']; local Time = A['Time']; if utilities.is_set (Minutes) then if utilities.is_set (Time) then --TODO: make a function for this and similar? utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'time')}); end Position = " " .. Minutes .. " " .. cfg.messages['minutes']; else if utilities.is_set (Time) then local TimeCaption = A['TimeCaption'] if not utilities.is_set (TimeCaption) then TimeCaption = cfg.messages['event']; if sepc ~= '.' then TimeCaption = TimeCaption:lower(); end end Position = " " .. TimeCaption .. " " .. Time; end end else Position = " " .. Position; At = ''; end Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase); At = utilities.is_set (At) and (sepc .. " " .. At) or ""; Position = utilities.is_set (Position) and (sepc .. " " .. Position) or ""; if config.CitationClass == 'map' then local Sections = A['Sections']; -- Section (singular) is an alias of Chapter so set earlier local Inset = A['Inset']; if utilities.is_set ( Inset ) then Inset = sepc .. " " .. wrap_msg ('inset', Inset, use_lowercase); end if utilities.is_set ( Sections ) then Section = sepc .. " " .. wrap_msg ('sections', Sections, use_lowercase); elseif utilities.is_set ( Section ) then Section = sepc .. " " .. wrap_msg ('section', Section, use_lowercase); end At = At .. Inset .. Section; end local Others = A['Others']; if utilities.is_set (Others) and 0 == #a and 0 == #e then -- add maint cat when |others= has value and used without |author=, |editor= if config.CitationClass == "AV-media-notes" or config.CitationClass == "audio-visual" then -- special maint for AV/M which has a lot of 'false' positives right now utilities.set_message ('maint_others_avm') else utilities.set_message ('maint_others'); end end Others = utilities.is_set (Others) and (sepc .. " " .. Others) or ""; if utilities.is_set (Translators) then Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc); end if utilities.is_set (Interviewers) then Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc); end local TitleNote = A['TitleNote']; TitleNote = utilities.is_set (TitleNote) and (sepc .. " " .. TitleNote) or ""; if utilities.is_set (Edition) then if Edition:match ('%f[%a][Ee]d%n?%.?$') or Edition:match ('%f[%a][Ee]dition$') then -- Ed, ed, Ed., ed., Edn, edn, Edn., edn. utilities.set_message ('err_extra_text_edition'); -- add error message end Edition = " " .. wrap_msg ('edition', Edition); else Edition = ''; end Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum local Agency = A['Agency']; Agency = utilities.is_set (Agency) and wrap_msg ('agency', {sepc, Agency}) or ""; Volume = format_volume_issue (Volume, Issue, ArticleNumber, config.CitationClass, Periodical_origin, sepc, use_lowercase); if utilities.is_set (AccessDate) then local retrv_text = " " .. cfg.messages['retrieved'] AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if mode is cs2, lower case AccessDate = utilities.substitute (retrv_text, AccessDate); -- add retrieved text AccessDate = utilities.substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates end if utilities.is_set (ID) then ID = sepc .. " " .. ID; end local Docket = A['Docket']; if "thesis" == config.CitationClass and utilities.is_set (Docket) then ID = sepc .. " Docket " .. Docket .. ID; end if "report" == config.CitationClass and utilities.is_set (Docket) then -- for cite report when |docket= is set ID = sepc .. ' ' .. Docket; -- overwrite ID even if |id= is set end if utilities.is_set (URL) then URL = " " .. external_link( URL, nil, URL_origin, UrlAccess ); end local Quote = A['Quote']; local TransQuote = A['TransQuote']; local ScriptQuote = A['ScriptQuote']; if utilities.is_set (Quote) or utilities.is_set (TransQuote) or utilities.is_set (ScriptQuote) then if utilities.is_set (Quote) then if Quote:sub(1, 1) == '"' and Quote:sub(-1, -1) == '"' then -- if first and last characters of quote are quote marks Quote = Quote:sub(2, -2); -- strip them off end end Quote = kern_quotes (Quote); -- kern if needed Quote = utilities.wrap_style ('quoted-text', Quote ); -- wrap in <q>...</q> tags if utilities.is_set (ScriptQuote) then Quote = script_concatenate (Quote, ScriptQuote, 'script-quote'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after quote is wrapped end if utilities.is_set (TransQuote) then if TransQuote:sub(1, 1) == '"' and TransQuote:sub(-1, -1) == '"' then -- if first and last characters of |trans-quote are quote marks TransQuote = TransQuote:sub(2, -2); -- strip them off end Quote = Quote .. " " .. utilities.wrap_style ('trans-quoted-title', TransQuote ); end if utilities.is_set (QuotePage) or utilities.is_set (QuotePages) then -- add page prefix local quote_prefix = ''; if utilities.is_set (QuotePage) then extra_text_in_page_check (QuotePage, 'quote-page'); -- add to maint cat if |quote-page= value begins with what looks like p., pp., etc. if not NoPP then quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, QuotePage}), '', '', ''; else quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, QuotePage}), '', '', ''; end elseif utilities.is_set (QuotePages) then extra_text_in_page_check (QuotePages, 'quote-pages'); -- add to maint cat if |quote-pages= value begins with what looks like p., pp., etc. if tonumber(QuotePages) ~= nil and not NoPP then -- if only digits, assume single page quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, QuotePages}), '', ''; elseif not NoPP then quote_prefix = utilities.substitute (cfg.messages['pp-prefix'], {sepc, QuotePages}), '', ''; else quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, QuotePages}), '', ''; end end Quote = quote_prefix .. ": " .. Quote; else Quote = sepc .. " " .. Quote; end PostScript = ""; -- cs1|2 does not supply terminal punctuation when |quote= is set end -- We check length of PostScript here because it will have been nuked by -- the quote parameters. We'd otherwise emit a message even if there wasn't -- a displayed postscript. -- TODO: Should the max size (1) be configurable? -- TODO: Should we check a specific pattern? if utilities.is_set(PostScript) and mw.ustring.len(PostScript) > 1 then utilities.set_message ('maint_postscript') end local Archived; if utilities.is_set (ArchiveURL) then local arch_text; if not utilities.is_set (ArchiveDate) then utilities.set_message ('err_archive_missing_date'); ArchiveDate = ''; -- empty string for concatenation end if "live" == UrlStatus then arch_text = cfg.messages['archived']; if sepc ~= "." then arch_text = arch_text:lower() end if utilities.is_set (ArchiveDate) then Archived = sepc .. ' ' .. utilities.substitute ( cfg.messages['archived-live'], {external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil) .. ArchiveFormat, ArchiveDate } ); else Archived = ''; end if not utilities.is_set (OriginalURL) then utilities.set_message ('err_archive_missing_url'); Archived = ''; -- empty string for concatenation end elseif utilities.is_set (OriginalURL) then -- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown' if utilities.in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then arch_text = cfg.messages['archived-unfit']; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. ' ' .. arch_text .. ArchiveDate; -- format already styled if 'bot: unknown' == UrlStatus then utilities.set_message ('maint_bot_unknown'); -- and add a category if not already added else utilities.set_message ('maint_unfit'); -- and add a category if not already added end else -- UrlStatus is empty, 'dead' arch_text = cfg.messages['archived-dead']; if sepc ~= "." then arch_text = arch_text:lower() end if utilities.is_set (ArchiveDate) then Archived = sepc .. " " .. utilities.substitute ( arch_text, { external_link( OriginalURL, cfg.messages['original'], OriginalURL_origin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled else Archived = ''; -- unset for concatenation end end else -- OriginalUrl not set arch_text = cfg.messages['archived-missing']; if sepc ~= "." then arch_text = arch_text:lower() end utilities.set_message ('err_archive_missing_url'); Archived = ''; -- empty string for concatenation end elseif utilities.is_set (ArchiveFormat) then Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message else Archived = ''; end local Lay = ''; local LaySource = A['LaySource']; local LayURL = A['LayURL']; local LayFormat = A['LayFormat']; LayFormat = style_format (LayFormat, LayURL, 'lay-format', 'lay-url'); if utilities.is_set (LayURL) then if utilities.is_set (LayDate) then LayDate = " (" .. LayDate .. ")" end if utilities.is_set (LaySource) then LaySource = " &ndash; ''" .. utilities.safe_for_italics (LaySource) .. "''"; else LaySource = ""; end if sepc == '.' then Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate else Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate end elseif utilities.is_set (LayFormat) then -- Test if |lay-format= is given without giving a |lay-url= Lay = sepc .. LayFormat; -- if set and LayURL not set, then LayFormat has error message end local TranscriptURL = A['TranscriptURL'] local TranscriptFormat = A['TranscriptFormat']; TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl'); local Transcript = A['Transcript']; local TranscriptURL_origin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL if utilities.is_set (Transcript) then if utilities.is_set (TranscriptURL) then Transcript = external_link( TranscriptURL, Transcript, TranscriptURL_origin, nil ); end Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat; elseif utilities.is_set (TranscriptURL) then Transcript = external_link( TranscriptURL, nil, TranscriptURL_origin, nil ); end local Publisher; if utilities.is_set (PublicationDate) then PublicationDate = wrap_msg ('published', PublicationDate); end if utilities.is_set (PublisherName) then if utilities.is_set (PublicationPlace) then Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate; else Publisher = sepc .. " " .. PublisherName .. PublicationDate; end elseif utilities.is_set (PublicationPlace) then Publisher= sepc .. " " .. PublicationPlace .. PublicationDate; else Publisher = PublicationDate; end local TransPeriodical = A['TransPeriodical']; local TransPeriodical_origin = A:ORIGIN ('TransPeriodical'); -- Several of the above rely upon detecting this as nil, so do it last. if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then if utilities.is_set (Title) or utilities.is_set (TitleNote) then Periodical = sepc .. " " .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin); else Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin); end end local Language = A['Language']; if utilities.is_set (Language) then Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc. else Language=''; -- language not specified so make sure this is an empty string; --[[ TODO: need to extract the wrap_msg from language_parameter so that we can solve parentheses bunching problem with Format/Language/TitleType ]] end --[[ Handle the oddity that is cite speech. This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided). ]] if "speech" == config.CitationClass then -- cite speech only TitleNote = TitleType; -- move TitleType to TitleNote so that it renders ahead of |event= TitleType = ''; -- and unset if utilities.is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter if utilities.is_set (Conference) then -- and if |event= is set Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering end end end -- Piece all bits together at last. Here, all should be non-nil. -- We build things this way because it is more efficient in LUA -- not to keep reassigning to the same string variable over and over. local tcommon; local tcommon2; -- used for book cite when |contributor= is set if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then if not (utilities.is_set (Authors) or utilities.is_set (Editors)) then Others = Others:gsub ('^' .. sepc .. ' ', ''); -- when no authors and no editors, strip leading sepc and space end if utilities.is_set (Others) then Others = safe_join ({Others, sepc .. " "}, sepc) end -- add terminal punctuation & space; check for dup sepc; TODO why do we need to do this here? tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc ); elseif utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites if utilities.is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc. tcommon = safe_join( {Title, TitleNote}, sepc ); -- author and other stuff will come after this and before tcommon2 tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc ); else tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc ); end elseif 'map' == config.CitationClass then -- special cases for cite map if utilities.is_set (Chapter) then -- map in a book; TitleType is part of Chapter tcommon = safe_join( {Title, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc ); elseif utilities.is_set (Periodical) then -- map in a periodical tcommon = safe_join( {Title, TitleType, Format, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc ); else -- a sheet or stand-alone map tcommon = safe_join( {Title, TitleType, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc ); end elseif 'episode' == config.CitationClass then -- special case for cite episode tcommon = safe_join( {Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc ); else -- all other CS1 templates tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc ); end if #ID_list > 0 then ID_list = safe_join( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc ); else ID_list = ID; end local Via = A['Via']; Via = utilities.is_set (Via) and wrap_msg ('via', Via) or ''; local idcommon; if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Lay, Quote }, sepc ); else idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Lay, Quote }, sepc ); end local text; local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At; local OrigDate = A['OrigDate']; OrigDate = utilities.is_set (OrigDate) and wrap_msg ('origdate', OrigDate) or ''; if utilities.is_set (Date) then if utilities.is_set (Authors) or utilities.is_set (Editors) then -- date follows authors or editors when authors not set Date = " (" .. Date .. ")" .. OrigDate .. sepc .. " "; -- in parentheses else -- neither of authors and editors set if (string.sub(tcommon, -1, -1) == sepc) then -- if the last character of tcommon is sepc Date = " " .. Date .. OrigDate; -- Date does not begin with sepc else Date = sepc .. " " .. Date .. OrigDate; -- Date begins with sepc end end end if utilities.is_set (Authors) then if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Authors termination Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space end if utilities.is_set (Editors) then local in_text = " "; local post_text = ""; if utilities.is_set (Chapter) and 0 == #c then in_text = in_text .. cfg.messages['in'] .. " " if (sepc ~= '.') then in_text = in_text:lower() -- lowercase for cs2 end end if EditorCount <= 1 then post_text = " (" .. cfg.messages['editor'] .. ")"; -- be consistent with no-author, no-date case else post_text = " (" .. cfg.messages['editors'] .. ")"; end Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space end if utilities.is_set (Contributors) then -- book cite and we're citing the intro, preface, etc. local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' '; if (sepc ~= '.') then by_text = by_text:lower() end -- lowercase for cs2 Authors = by_text .. Authors; -- author follows title so tweak it here if utilities.is_set (Editors) and utilities.is_set (Date) then -- when Editors make sure that Authors gets terminated Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space end if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Contributors termination Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space end text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc ); else text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc ); end elseif utilities.is_set (Editors) then if utilities.is_set (Date) then if EditorCount <= 1 then Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editor']; else Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editors']; end else if EditorCount <= 1 then Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " " else Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " " end end text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc ); else if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc ); else text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc ); end end if utilities.is_set (PostScript) and PostScript ~= sepc then text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc. text = text:sub(1, -sepc:len() - 1); end text = safe_join( {text, PostScript}, sepc ); -- Now enclose the whole thing in a <cite> element local options_t = {}; options_t.class = cite_class_attribute_make (config.CitationClass, Mode); local Ref = is_valid_parameter_value (A['Ref'], A:ORIGIN('Ref'), cfg.keywords_lists['ref'], nil, true); -- nil when |ref=harv; A['Ref'] else if 'none' ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ''] then local namelist_t = {}; -- holds selected contributor, author, editor name list local year = first_set ({Year, anchor_year}, 2); -- Year first for legacy citations and for YMD dates that require disambiguation if #c > 0 then -- if there is a contributor list namelist_t = c; -- select it elseif #a > 0 then -- or an author list namelist_t = a; elseif #e > 0 then -- or an editor list namelist_t = e; end local citeref_id; if #namelist_t > 0 then -- if there are names in namelist_t citeref_id = make_citeref_id (namelist_t, year); -- go make the CITEREF anchor if mw.uri.anchorEncode (citeref_id) == ((Ref and mw.uri.anchorEncode (Ref)) or '') then -- Ref may already be encoded (by {{sfnref}}) so citeref_id must be encoded before comparison utilities.set_message ('maint_ref_duplicates_default'); end else citeref_id = ''; -- unset end options_t.id = Ref or citeref_id; end if string.len (text:gsub('%b<>', '')) <= 2 then -- remove html and html-like tags; then get length of what remains; z.error_cats_t = {}; -- blank the categories list z.error_msgs_t = {}; -- blank the error messages list OCinSoutput = nil; -- blank the metadata string text = ''; -- blank the the citation utilities.set_message ('err_empty_citation'); -- set empty citation message and category end local render_t = {}; -- here we collect the final bits for concatenation into the rendered citation if utilities.is_set (options_t.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags table.insert (render_t, utilities.substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options_t.id), mw.text.nowiki(options_t.class), text})); -- when |ref= is set or when there is a namelist else table.insert (render_t, utilities.substitute (cfg.presentation['cite'], {mw.text.nowiki(options_t.class), text})); -- when |ref=none or when namelist_t empty and |ref= is missing or is empty end if OCinSoutput then -- blanked when citation is 'empty' so don't bother to add boilerplate metadata span table.insert (render_t, utilities.substitute (cfg.presentation['ocins'], OCinSoutput)); -- format and append metadata to the citation end local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass); local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]'; local msg_prefix = '<code class="cs1-code">{{' .. template_link .. '}}</code>: '; if 0 ~= #z.error_msgs_t then mw.addWarning (utilities.substitute (cfg.messages.warning_msg_e, template_link)); table.insert (render_t, ' '); -- insert a space between citation and its error messages table.sort (z.error_msgs_t); -- sort the error messages list; sorting includes wrapping <span> and <code> tags; hidden-error sorts ahead of visible-error local hidden = true; -- presume that the only error messages emited by this template are hidden for _, v in ipairs (z.error_msgs_t) do -- spin through the list of error messages if v:find ('cs1-visible-error', 1, true) then -- look for the visible error class name hidden = false; -- found one; so don't hide the error message prefix break; -- and done because no need to look further end end z.error_msgs_t[1] = table.concat ({utilities.error_comment (msg_prefix, hidden), z.error_msgs_t[1]}); -- add error message prefix to first error message to prevent extraneous punctuation table.insert (render_t, table.concat (z.error_msgs_t, '; ')); -- make a big string of error messages and add it to the rendering end if 0 ~= #z.maint_cats_t then mw.addWarning (utilities.substitute (cfg.messages.warning_msg_m, template_link)); table.sort (z.maint_cats_t); -- sort the maintenance messages list local maint_msgs_t = {}; -- here we collect all of the maint messages if 0 == #z.error_msgs_t then -- if no error messages table.insert (maint_msgs_t, msg_prefix); -- insert message prefix in maint message livery end for _, v in ipairs( z.maint_cats_t ) do -- append maintenance categories table.insert (maint_msgs_t, -- assemble new maint message and add it to the maint_msgs_t table table.concat ({v, ' (', utilities.substitute (cfg.messages[':cat wikilink'], v), ')'}) ); end table.insert (render_t, utilities.substitute (cfg.presentation['hidden-maint'], table.concat (maint_msgs_t, ' '))); -- wrap the group of maint messages with proper presentation and save end if not no_tracking_cats then for _, v in ipairs (z.error_cats_t) do -- append error categories table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); end for _, v in ipairs (z.maint_cats_t) do -- append maintenance categories table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); end for _, v in ipairs (z.prop_cats_t) do -- append properties categories table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); end end return table.concat (render_t); -- make a big string and done end --[[--------------------------< V A L I D A T E >-------------------------------------------------------------- Looks for a parameter's name in one of several whitelists. Parameters in the whitelist can have three values: true - active, supported parameters false - deprecated, supported parameters nil - unsupported parameters ]] local function validate (name, cite_class, empty) local name = tostring (name); local enum_name; -- for enumerated parameters, is name with enumerator replaced with '#' local state; local function state_test (state, name) -- local function to do testing of state values if true == state then return true; end -- valid actively supported parameter if false == state then if empty then return nil; end -- empty deprecated parameters are treated as unknowns deprecated_parameter (name); -- parameter is deprecated but still supported return true; end if 'tracked' == state then local base_name = name:gsub ('%d', ''); -- strip enumerators from parameter names that have them to get the base name utilities.add_prop_cat ('tracked-param', {base_name}, base_name); -- add a properties category; <base_name> modifies <key> return true; end return nil; end if name:find ('#') then -- # is a cs1|2 reserved character so parameters with # not permitted return nil; end if utilities.in_array (cite_class, whitelist.preprint_template_list ) then -- limited parameter sets allowed for these templates state = whitelist.limited_basic_arguments[name]; if true == state_test (state, name) then return true; end state = whitelist.preprint_arguments[cite_class][name]; -- look in the parameter-list for the template identified by cite_class if true == state_test (state, name) then return true; end -- limited enumerated parameters list enum_name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) state = whitelist.limited_numbered_arguments[enum_name]; if true == state_test (state, name) then return true; end return false; -- not supported because not found or name is set to nil end -- end limited parameter-set templates if utilities.in_array (cite_class, whitelist.unique_param_template_list) then -- experiment for template-specific parameters for templates that accept parameters from the basic argument list state = whitelist.unique_arguments[cite_class][name]; -- look in the template-specific parameter-lists for the template identified by cite_class if true == state_test (state, name) then return true; end end -- if here, fall into general validation state = whitelist.basic_arguments[name]; -- all other templates; all normal parameters allowed if true == state_test (state, name) then return true; end -- all enumerated parameters allowed enum_name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) state = whitelist.numbered_arguments[enum_name]; if true == state_test (state, name) then return true; end return false; -- not supported because not found or name is set to nil end --[=[-------------------------< I N T E R _ W I K I _ C H E C K >---------------------------------------------- check <value> for inter-language interwiki-link markup. <prefix> must be a MediaWiki-recognized language code. when these values have the form (without leading colon): [[<prefix>:link|label]] return label as plain-text [[<prefix>:link]] return <prefix>:link as plain-text return value as is else ]=] local function inter_wiki_check (parameter, value) local prefix = value:match ('%[%[(%a+):'); -- get an interwiki prefix if one exists local _; if prefix and cfg.inter_wiki_map[prefix:lower()] then -- if prefix is in the map, needs preceding colon so utilities.set_message ('err_bad_paramlink', parameter); -- emit an error message _, value, _ = utilities.is_wikilink (value); -- extract label portion from wikilink end return value; end --[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------ Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a parameter that is missing its pipe. There are two tests made: {{cite ... |title=Title access-date=2016-03-17}} -- the first parameter has a value and whitespace separates that value from the missing pipe parameter name {{cite ... |title=access-date=2016-03-17}} -- the first parameter has no value (whitespace after the first = is trimmed by MediaWiki) cs1|2 shares some parameter names with XML/HTML attributes: class=, title=, etc. To prevent false positives XML/HTML tags are removed before the search. If a missing pipe is detected, this function adds the missing pipe maintenance category. ]] local function missing_pipe_check (parameter, value) local capture; value = value:gsub ('%b<>', ''); -- remove XML/HTML tags because attributes: class=, title=, etc. capture = value:match ('%s+(%a[%w%-]+)%s*=') or value:match ('^(%a[%w%-]+)%s*='); -- find and categorize parameters with possible missing pipes if capture and validate (capture) then -- if the capture is a valid parameter name utilities.set_message ('err_missing_pipe', parameter); end end --[[--------------------------< H A S _ E X T R A N E O U S _ P U N C T >-------------------------------------- look for extraneous terminal punctuation in most parameter values; parameters listed in skip table are not checked ]] local function has_extraneous_punc (param, value) if 'number' == type (param) then return; end param = param:gsub ('%d+', '#'); -- enumerated name-list mask params allow terminal punct; normalize if cfg.punct_skip[param] then return; -- parameter name found in the skip table so done end if value:match ('[,;:]$') then utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat end if value:match ('^=') then -- sometimes an extraneous '=' character appears ... utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat end end --[[--------------------------< H A S _ E X T R A N E O U S _ U R L >------------------------------------------ look for extraneous url parameter values; parameters listed in skip table are not checked ]] local function has_extraneous_url (url_param_t) local url_error_t = {}; check_for_url (url_param_t, url_error_t); -- extraneous url check if 0 ~= #url_error_t then -- non-zero when there are errors table.sort (url_error_t); utilities.set_message ('err_param_has_ext_link', {utilities.make_sep_list (#url_error_t, url_error_t)}); -- add this error message end end --[[--------------------------< C I T A T I O N >-------------------------------------------------------------- This is used by templates such as {{cite book}} to create the actual citation text. ]] local function citation(frame) Frame = frame; -- save a copy in case we need to display an error message in preview mode local config = {}; -- table to store parameters from the module {{#invoke:}} for k, v in pairs( frame.args ) do -- get parameters from the {{#invoke}} frame config[k] = v; -- args[k] = v; -- crude debug support that allows us to render a citation from module {{#invoke:}}; skips parameter validation; TODO: keep? end -- i18n: set the name that your wiki uses to identify sandbox subpages from sandbox template invoke (or can be set here) local sandbox = ((config.SandboxPath and '' ~= config.SandboxPath) and config.SandboxPath) or '/sandbox'; -- sandbox path from {{#invoke:Citation/CS1/sandbox|citation|SandboxPath=/...}} is_sandbox = nil ~= string.find (frame:getTitle(), sandbox, 1, true); -- is this invoke the sandbox module? sandbox = is_sandbox and sandbox or ''; -- use i18n sandbox to load sandbox modules when this module is the sandox; live modules else local pframe = frame:getParent() local styles; cfg = mw.loadData ('Module:Citation/CS1/Configuration' .. sandbox); -- load sandbox versions of support modules when {{#invoke:Citation/CS1/sandbox|...}}; live modules else whitelist = mw.loadData ('Module:Citation/CS1/Whitelist' .. sandbox); utilities = require ('Module:Citation/CS1/Utilities' .. sandbox); validation = require ('Module:Citation/CS1/Date_validation' .. sandbox); identifiers = require ('Module:Citation/CS1/Identifiers' .. sandbox); metadata = require ('Module:Citation/CS1/COinS' .. sandbox); styles = 'Module:Citation/CS1' .. sandbox .. '/styles.css'; utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the selected cfg tables identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module validation.set_selected_modules (cfg, utilities); -- so that functions in Date validataion can see selected cfg tables and the selected Utilities module metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities is_preview_mode = not utilities.is_set (frame:preprocess ('{{REVISIONID}}')); local args = {}; -- table where we store all of the template's arguments local suggestions = {}; -- table where we store suggestions if we need to loadData them local error_text; -- used as a flag local capture; -- the single supported capture when matching unknown parameters using patterns local empty_unknowns = {}; -- sequence table to hold empty unknown params for error message listing for k, v in pairs( pframe.args ) do -- get parameters from the parent (template) frame v = mw.ustring.gsub (v, '^%s*(.-)%s*$', '%1'); -- trim leading/trailing whitespace; when v is only whitespace, becomes empty string if v ~= '' then if ('string' == type (k)) then k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits); -- for enumerated parameters, translate 'local' digits to Western 0-9 end if not validate( k, config.CitationClass ) then if type (k) ~= 'string' then -- exclude empty numbered parameters if v:match("%S+") ~= nil then error_text = utilities.set_message ('err_text_ignored', {v}); end elseif validate (k:lower(), config.CitationClass) then error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, k:lower()}); -- suggest the lowercase version of the parameter else if nil == suggestions.suggestions then -- if this table is nil then we need to load it suggestions = mw.loadData ('Module:Citation/CS1/Suggestions' .. sandbox); --load sandbox version of suggestion module when {{#invoke:Citation/CS1/sandbox|...}}; live module else end for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter capture = k:match (pattern); -- the whole match if no capture in pattern else the capture if a match if capture then -- if the pattern matches param = utilities.substitute (param, capture); -- add the capture to the suggested parameter (typically the enumerator) if validate (param, config.CitationClass) then -- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists) error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, param}); -- set the suggestion error message else error_text = utilities.set_message ('err_parameter_ignored', {k}); -- suggested param not supported by this template v = ''; -- unset end end end if not utilities.is_set (error_text) then -- couldn't match with a pattern, is there an explicit suggestion? if (suggestions.suggestions[ k:lower() ] ~= nil) and validate (suggestions.suggestions[ k:lower() ], config.CitationClass) then utilities.set_message ('err_parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower() ]}); else utilities.set_message ('err_parameter_ignored', {k}); v = ''; -- unset value assigned to unrecognized parameters (this for the limited parameter lists) end end end end args[k] = v; -- save this parameter and its value elseif not utilities.is_set (v) then -- for empty parameters if not validate (k, config.CitationClass, true) then -- is this empty parameter a valid parameter k = ('' == k) and '(empty string)' or k; -- when k is empty string (or was space(s) trimmed to empty string), replace with descriptive text table.insert (empty_unknowns, utilities.wrap_style ('parameter', k)); -- format for error message and add to the list end -- crude debug support that allows us to render a citation from module {{#invoke:}} TODO: keep? -- elseif args[k] ~= nil or (k == 'postscript') then -- when args[k] has a value from {{#invoke}} frame (we don't normally do that) -- args[k] = v; -- overwrite args[k] with empty string from pframe.args[k] (template frame); v is empty string here end -- not sure about the postscript bit; that gets handled in parameter validation; historical artifact? end if 0 ~= #empty_unknowns then -- create empty unknown error message utilities.set_message ('err_param_unknown_empty', { 1 == #empty_unknowns and '' or 's', utilities.make_sep_list (#empty_unknowns, empty_unknowns) }); end local url_param_t = {}; for k, v in pairs( args ) do if 'string' == type (k) then -- don't evaluate positional parameters has_invisible_chars (k, v); -- look for invisible characters end has_extraneous_punc (k, v); -- look for extraneous terminal punctuation in parameter values missing_pipe_check (k, v); -- do we think that there is a parameter that is missing a pipe? args[k] = inter_wiki_check (k, v); -- when language interwiki-linked parameter missing leading colon replace with wiki-link label if 'string' == type (k) and not cfg.url_skip[k] then -- when parameter k is not positional and not in url skip table url_param_t[k] = v; -- make a parameter/value list for extraneous url check end end has_extraneous_url (url_param_t); -- look for url in parameter values where a url does not belong return table.concat ({ frame:extensionTag ('templatestyles', '', {src=styles}), citation0( config, args) }); end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return {citation = citation}; 9bfe095ac3f64719c64a17280b76d0add203ad61 Module:Citation/CS1/Configuration 828 397 795 794 2023-07-10T15:57:45Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1/Configuration]] Scribunto text/plain local lang_obj = mw.language.getContentLanguage(); -- make a language object for the local language; used here for languages and dates --[[--------------------------< U N C A T E G O R I Z E D _ N A M E S P A C E S >------------------------------ List of namespaces identifiers for namespaces that will not be included in citation error categories. Same as setting notracking = true by default. For wikis that have a current version of Module:cs1 documentation support, this #invoke will return an unordered list of namespace names and their associated identifiers: {{#invoke:cs1 documentation support|uncategorized_namespace_lister|all=<anything>}} ]] uncategorized_namespaces_t = {[2]=true}; -- init with user namespace id for k, _ in pairs (mw.site.talkNamespaces) do -- add all talk namespace ids uncategorized_namespaces_t[k] = true; end local uncategorized_subpages = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'}; -- list of Lua patterns found in page names of pages we should not categorize --[[--------------------------< M E S S A G E S >-------------------------------------------------------------- Translation table The following contains fixed text that may be output as part of a citation. This is separated from the main body to aid in future translations of this module. ]] local messages = { ['agency'] = '$1 $2', -- $1 is sepc, $2 is agency ['archived-dead'] = 'Archived from $1 on $2', ['archived-live'] = '$1 from the original on $2', ['archived-missing'] = 'Archived from the original $1 on $2', ['archived-unfit'] = 'Archived from the original on ', ['archived'] = 'Archived', ['by'] = 'By', -- contributions to authored works: introduction, foreword, afterword ['cartography'] = 'Cartography by $1', ['editor'] = 'ed.', ['editors'] = 'eds.', ['edition'] = '($1&nbsp;ed.)', ['episode'] = 'Episode $1', ['et al'] = 'et&nbsp;al.', ['in'] = 'In', -- edited works ['inactive'] = 'inactive', ['inset'] = '$1 inset', ['interview'] = 'Interviewed by $1', ['lay summary'] = 'Lay summary', ['mismatch'] = '<code class="cs1-code">&#124;$1=</code> / <code class="cs1-code">&#124;$2=</code> mismatch', -- $1 is year param name; $2 is date param name ['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]:&nbsp;$1', ['notitle'] = 'No title', -- for |title=(()) and (in the future) |title=none ['original'] = 'the original', ['origdate'] = ' [$1]', ['published'] = ' (published $1)', ['retrieved'] = 'Retrieved $1', ['season'] = 'Season $1', ['section'] = '§&nbsp;$1', ['sections'] = '§§&nbsp;$1', ['series'] = '$1 $2', -- $1 is sepc, $2 is series ['seriesnum'] = 'Series $1', ['translated'] = 'Translated by $1', ['type'] = ' ($1)', -- for titletype ['written'] = 'Written at $1', ['vol'] = '$1 Vol.&nbsp;$2', -- $1 is sepc; bold journal style volume is in presentation{} ['vol-no'] = '$1 Vol.&nbsp;$2, no.&nbsp;$3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization) ['issue'] = '$1 No.&nbsp;$2', -- $1 is sepc ['art'] = '$1 Art.&nbsp;$2', -- $1 is sepc; for {{cite conference}} only ['vol-art'] = '$1 Vol.&nbsp;$2, art.&nbsp;$3', -- sepc, volume, article-number; for {{cite conference}} only ['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{} ['j-issue'] = ' ($1)', ['j-article-num'] = ' $1', -- TODO: any punctuation here? static text? ['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc ['p-prefix'] = "$1 p.&nbsp;$2", -- $1 is sepc ['pp-prefix'] = "$1 pp.&nbsp;$2", -- $1 is sepc ['j-page(s)'] = ': $1', -- same for page and pages ['sheet'] = '$1 Sheet&nbsp;$2', -- $1 is sepc ['sheets'] = '$1 Sheets&nbsp;$2', -- $1 is sepc ['j-sheet'] = ': Sheet&nbsp;$1', ['j-sheets'] = ': Sheets&nbsp;$1', ['language'] = '(in $1)', ['via'] = " &ndash; via $1", ['event'] = 'Event occurs at', ['minutes'] = 'minutes in', -- Determines the location of the help page ['help page link'] = 'Help:CS1 errors', ['help page label'] = 'help', -- categories ['cat wikilink'] = '[[Category:$1]]', -- $1 is the category name [':cat wikilink'] = '[[:Category:$1|link]]', -- category name as maintenance message wikilink; $1 is the category name -- Internal errors (should only occur if configuration is bad) ['undefined_error'] = 'Called with an undefined error condition', ['unknown_ID_key'] = 'Unrecognized ID key: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{} ['unknown_ID_access'] = 'Unrecognized ID access keyword: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{} ['unknown_argument_map'] = 'Argument map not defined for this variable', ['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty', ['warning_msg_e'] = '<span style="color:#d33">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">&#123;{$1}}</code> templates have errors</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link ['warning_msg_m'] = '<span style="color:#3a3">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">&#123;{$1}}</code> templates have maintenance messages</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link } --[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------ this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the template's documentation when an article is displayed in preview mode. Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here. ]] local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n ['audio-visual'] = 'AV media', ['AV-media-notes'] = 'AV media notes', ['encyclopaedia'] = 'encyclopedia', ['mailinglist'] = 'mailing list', ['pressrelease'] = 'press release' } --[=[-------------------------< E T _ A L _ P A T T E R N S >-------------------------------------------------- This table provides Lua patterns for the phrase "et al" and variants in name text (author, editor, etc.). The main module uses these to identify and emit the 'etal' message. ]=] local et_al_patterns = { "[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme "[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match) "[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al. "%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form "%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax) "[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form } --[[--------------------------< P R E S E N T A T I O N >------------------------ Fixed presentation markup. Originally part of citation_config.messages it has been moved into its own, more semantically correct place. ]] local presentation = { -- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display ['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>', ['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>', ['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>', ['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS ['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like ['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute ['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist ['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc. ['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc ['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored) -- various access levels, for |access=, |doi-access=, |arxiv=, ... -- narrow no-break space &#8239; may work better than nowrap CSS. Or not? Browser support? ['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon ['free'] = {class='cs1-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css ['registration'] = {class='cs1-lock-registration', title='Free registration required'}, ['limited'] = {class='cs1-lock-limited', title='Free access subject to limited trial, subscription normally required'}, ['subscription'] = {class='cs1-lock-subscription', title='Paid subscription required'}, ['interwiki-icon'] = '<span class="$1" title="$2">$3</span>', ['class-wikisource'] = 'cs1-ws-icon', ['italic-title'] = "''$1''", ['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark ['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark ['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span> ['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space) ['ocins'] = '<span title="$1" class="Z3988"></span>', ['parameter'] = '<code class="cs1-code">&#124;$1=</code>', ['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character ['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string) ['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content ['quoted-title'] = '"$1"', ['sep_cs1'] = '.', -- CS1 element separator ['sep_cs2'] = ',', -- CS2 separator ['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon ['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items ['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names ['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space> ['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma ['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space ['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end ['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items ['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items ['trans-italic-title'] = "&#91;''$1''&#93;", ['trans-quoted-title'] = "&#91;$1&#93;", -- for |trans-title= and |trans-quote= ['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{} } --[[--------------------------< A L I A S E S >--------------------------------- Aliases table for commonly passed parameters. Parameter names on the right side in the assignments in this table must have been defined in the Whitelist before they will be recognized as valid parameter names ]] local aliases = { ['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot ['Agency'] = 'agency', ['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot ['ArchiveFormat'] = 'archive-format', ['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot ['ArticleNumber'] = 'article-number', ['ASINTLD'] = 'asin-tld', ['At'] = 'at', -- Used by InternetArchiveBot ['Authors'] = {'authors', 'people', 'credits'}, ['BookTitle'] = {'book-title', 'booktitle'}, ['Cartography'] = 'cartography', ['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'}, ['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format', 'article-format', 'section-format'}; ['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url', 'chapterurl'}, -- Used by InternetArchiveBot ['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access', 'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot ['Class'] = 'class', -- cite arxiv and arxiv identifier ['Collaboration'] = 'collaboration', ['Conference'] = {'conference', 'event'}, ['ConferenceFormat'] = 'conference-format', ['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot ['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only ['Degree'] = 'degree', ['DF'] = 'df', ['DisplayAuthors'] = {'display-authors', 'display-subjects'}, ['DisplayContributors'] = 'display-contributors', ['DisplayEditors'] = 'display-editors', ['DisplayInterviewers'] = 'display-interviewers', ['DisplayTranslators'] = 'display-translators', ['Docket'] = 'docket', ['DoiBroken'] = 'doi-broken-date', ['Edition'] = 'edition', ['Embargo'] = 'pmc-embargo-date', ['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only ['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode? ['Format'] = 'format', ['ID'] = {'id', 'ID'}, ['Inset'] = 'inset', ['Issue'] = {'issue', 'number'}, ['Language'] = {'language', 'lang'}, ['LayDate'] = 'lay-date', ['LayFormat'] = 'lay-format', ['LaySource'] = 'lay-source', ['LayURL'] = 'lay-url', ['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only ['Map'] = 'map', -- cite map only ['MapFormat'] = 'map-format', -- cite map only ['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot ['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot ['Minutes'] = 'minutes', ['Mode'] = 'mode', ['NameListStyle'] = 'name-list-style', ['Network'] = 'network', ['Newsgroup'] = 'newsgroup', -- cite newsgroup only ['NoPP'] = {'no-pp', 'nopp'}, ['NoTracking'] = {'no-tracking', 'template-doc-demo'}, ['Number'] = 'number', -- this case only for cite techreport ['OrigDate'] = {'orig-date', 'orig-year', 'origyear'}, ['Others'] = 'others', ['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot ['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot ['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'}, ['Place'] = {'place', 'location'}, ['PostScript'] = 'postscript', ['PublicationDate'] = {'publication-date', 'publicationdate'}, ['PublicationPlace'] = {'publication-place', 'publicationplace'}, ['PublisherName'] = {'publisher', 'institution'}, ['Quote'] = {'quote', 'quotation'}, ['QuotePage'] = 'quote-page', ['QuotePages'] = 'quote-pages', ['Ref'] = 'ref', ['Scale'] = 'scale', ['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry', 'script-article', 'script-section'}, ['ScriptMap'] = 'script-map', ['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-website', 'script-work'}, ['ScriptQuote'] = 'script-quote', ['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot ['Season'] = 'season', ['Sections'] = 'sections', -- cite map only ['Series'] = {'series', 'version'}, ['SeriesLink'] = {'series-link', 'serieslink'}, ['SeriesNumber'] = {'series-number', 'series-no'}, ['Sheet'] = 'sheet', -- cite map only ['Sheets'] = 'sheets', -- cite map only ['Station'] = 'station', ['Time'] = 'time', ['TimeCaption'] = 'time-caption', ['Title'] = 'title', -- Used by InternetArchiveBot ['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot ['TitleNote'] = 'department', ['TitleType'] = {'type', 'medium'}, ['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution', 'trans-entry', 'trans-section'}, ['Transcript'] = 'transcript', ['TranscriptFormat'] = 'transcript-format', ['TranscriptURL'] = {'transcript-url', 'transcripturl'}, -- Used by InternetArchiveBot ['TransMap'] = 'trans-map', -- cite map only ['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper', 'trans-periodical', 'trans-website', 'trans-work'}, ['TransQuote'] = 'trans-quote', ['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot ['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot ['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot ['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot ['Vauthors'] = 'vauthors', ['Veditors'] = 'veditors', ['Via'] = 'via', ['Volume'] = 'volume', ['Year'] = 'year', ['AuthorList-First'] = {"first#", "author-first#", "author#-first", "given#", "author-given#", "author#-given"}, ['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "surname#", "author-surname#", "author#-surname", "author#", "subject#", 'host#'}, ['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#", "subject#-link", "authorlink#", "author#link"}, ['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"}, ['ContributorList-First'] = {'contributor-first#', 'contributor#-first', 'contributor-given#', 'contributor#-given'}, ['ContributorList-Last'] = {'contributor-last#', 'contributor#-last', 'contributor-surname#', 'contributor#-surname', 'contributor#'}, ['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'}, ['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'}, ['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"}, ['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#", "editor#-surname", "editor#"}, ['EditorList-Link'] = {"editor-link#", "editor#-link"}, ['EditorList-Mask'] = {"editor-mask#", "editor#-mask"}, ['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first', 'interviewer-given#', 'interviewer#-given'}, ['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last', 'interviewer-surname#', 'interviewer#-surname', 'interviewer#'}, ['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'}, ['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'}, ['TranslatorList-First'] = {'translator-first#', 'translator#-first', 'translator-given#', 'translator#-given'}, ['TranslatorList-Last'] = {'translator-last#', 'translator#-last', 'translator-surname#', 'translator#-surname', 'translator#'}, ['TranslatorList-Link'] = {'translator-link#', 'translator#-link'}, ['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'}, } --[[--------------------------< P U N C T _ S K I P >--------------------------- builds a table of parameter names that the extraneous terminal punctuation check should not check. ]] local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value 'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters 'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators 'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous 'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'LayURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters } local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value 'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'LayURL', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls 'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls } local function build_skip_table (skip_t, meta_params) for _, meta_param in ipairs (meta_params) do -- for each meta parameter key local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name if 'string' == type (params) then skip_t[params] = 1; -- just a single parameter else for _, param in ipairs (params) do -- get the parameter name skip_t[param] = 1; -- add the parameter name to the skip table local count; param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters if 0 ~= count then -- if removed skip_t[param] = 1; -- add param name without enumerator marker end end end end return skip_t; end local punct_skip = {}; local url_skip = {}; --[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >---------- this is a list of tlds that are known to have single-letter second-level domain names. This list does not include ccTLDs which are accepted in is_domain_name(). ]] local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'org', 'today'}; --[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------ This table is primarily here to support internationalization. Translations in this table are used, for example, when an error message, category name, etc., is extracted from the English alias key. There may be other cases where this translation table may be useful. ]] local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143'; local special_case_translation = { ['AuthorList'] = 'authors list', -- used to assemble maintenance category names ['ContributorList'] = 'contributors list', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below ['EditorList'] = 'editors list', -- must match the names of the actual categories ['InterviewerList'] = 'interviewers list', -- this group or translations used by name_has_ed_markup() and name_has_mult_names() ['TranslatorList'] = 'translators list', -- Lua patterns to match pseudo-titles used by InternetArchiveBot and others as placeholder for unknown |title= value ['archived_copy'] = { -- used with CS1 maint: Archive[d] copy as title ['en'] = '^archived?%s+copy$', -- for English; translators: keep this because templates imported from en.wiki ['local'] = nil, -- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language }, -- Lua patterns to match generic titles; usually created by bots or reference filling tools -- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language -- generic titles and patterns in this table should be lowercase only -- leave ['local'] nil except when there is a matching generic title in your language -- boolean 'true' for plain-text searches; 'false' for pattern searches ['generic_titles'] = { ['accept'] = { }, ['reject'] = { {['en'] = {'^wayback%s+machine$', false}, ['local'] = nil}, {['en'] = {'are you a robot', true}, ['local'] = nil}, {['en'] = {'hugedomains.com', true}, ['local'] = nil}, {['en'] = {'^[%(%[{<]?no +title[>}%]%)]?$', false}, ['local'] = nil}, {['en'] = {'page not found', true}, ['local'] = nil}, {['en'] = {'subscribe to read', true}, ['local'] = nil}, {['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = nil}, {['en'] = {'website is for sale', true}, ['local'] = nil}, {['en'] = {'^404', false}, ['local'] = nil}, {['en'] = {'internet archive wayback machine', true}, ['local'] = nil}, {['en'] = {'log into facebook', true}, ['local'] = nil}, {['en'] = {'login • instagram', true}, ['local'] = nil}, {['en'] = {'redirecting...', true}, ['local'] = nil}, {['en'] = {'usurped title', true}, ['local'] = nil}, -- added by a GreenC bot {['en'] = {'webcite query result', true}, ['local'] = nil}, {['en'] = {'wikiwix\'s cache', true}, ['local'] = nil}, } }, -- boolean 'true' for plain-text searches, search string must be lowercase only -- boolean 'false' for pattern searches -- leave ['local'] nil except when there is a matching generic name in your language ['generic_names'] = { ['accept'] = { {['en'] = {'%[%[[^|]*%(author%) *|[^%]]*%]%]', false}, ['local'] = nil}, }, ['reject'] = { {['en'] = {'about us', true}, ['local'] = nil}, {['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil}, {['en'] = {'allmusic', true}, ['local'] = nil}, {['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = nil}, {['en'] = {'business', true}, ['local'] = nil}, {['en'] = {'cnn', true}, ['local'] = nil}, {['en'] = {'collaborator', true}, ['local'] = nil}, {['en'] = {'contributor', true}, ['local'] = nil}, {['en'] = {'contact us', true}, ['local'] = nil}, {['en'] = {'directory', true}, ['local'] = nil}, {['en'] = {'%f[%(%[][%(%[]%s*eds?%.?%s*[%)%]]?$', false}, ['local'] = nil}, {['en'] = {'[,%.%s]%f[e]eds?%.?$', false}, ['local'] = nil}, {['en'] = {'^eds?[%.,;]', false}, ['local'] = nil}, {['en'] = {'^[%(%[]%s*[Ee][Dd][Ss]?%.?%s*[%)%]]', false}, ['local'] = nil}, {['en'] = {'%f[%a][Ee]dited%f[%A]', false}, ['local'] = nil}, {['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = nil}, {['en'] = {'%f[%a]]Ee]mail%f[%A]', false}, ['local'] = nil}, {['en'] = {'facebook', true}, ['local'] = nil}, {['en'] = {'google', true}, ['local'] = nil}, {['en'] = {'home page', true}, ['local'] = nil}, {['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil}, {['en'] = {'instagram', true}, ['local'] = nil}, {['en'] = {'interviewer', true}, ['local'] = nil}, {['en'] = {'linkedIn', true}, ['local'] = nil}, {['en'] = {'^[Nn]ews$', false}, ['local'] = nil}, {['en'] = {'pinterest', true}, ['local'] = nil}, {['en'] = {'policy', true}, ['local'] = nil}, {['en'] = {'privacy', true}, ['local'] = nil}, {['en'] = {'reuters', true}, ['local'] = nil}, {['en'] = {'translator', true}, ['local'] = nil}, {['en'] = {'tumblr', true}, ['local'] = nil}, {['en'] = {'twitter', true}, ['local'] = nil}, {['en'] = {'site name', true}, ['local'] = nil}, {['en'] = {'statement', true}, ['local'] = nil}, {['en'] = {'submitted', true}, ['local'] = nil}, {['en'] = {'super.?user', false}, ['local'] = nil}, {['en'] = {'%f['..is_Latn..'][Uu]ser%f[^'..is_Latn..']', false}, ['local'] = nil}, {['en'] = {'verfasser', true}, ['local'] = nil}, } } } --[[--------------------------< D A T E _ N A M E S >---------------------------------------------------------- This table of tables lists local language date names and fallback English date names. The code in Date_validation will look first in the local table for valid date names. If date names are not found in the local table, the code will look in the English table. Because citations can be copied to the local wiki from en.wiki, the English is required when the date-name translation function date_name_xlate() is used. In these tables, season numbering is defined by Extended Date/Time Format (EDTF) Specification (https://www.loc.gov/standards/datetime/) which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using numbers 21-41. CS1|2 only supports generic seasons. EDTF does support the distinction between north and south hemisphere seasons but CS1|2 has no way to make that distinction. 33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each) The standard does not address 'named' dates so, for the purposes of CS1|2, Easter and Christmas are defined here as 98 and 99, which should be out of the ISO 8601 (EDTF) range of uses for a while. local_date_names_from_mediawiki is a boolean. When set to: true – module will fetch local month names from MediaWiki for both date_names['local']['long'] and date_names['local']['short'] false – module will *not* fetch local month names from MediaWiki Caveat lector: There is no guarantee that MediaWiki will provide short month names. At your wiki you can test the results of the MediaWiki fetch in the debug console with this command (the result is alpha sorted): =mw.dumpObject (p.date_names['local']) While the module can fetch month names from MediaWiki, it cannot fetch the quarter, season, and named date names from MediaWiki. Those must be translated manually. ]] local local_date_names_from_mediawiki = true; -- when false, manual translation required for date_names['local']['long'] and date_names['local']['short'] -- when true, module fetches long and short month names from MediaWiki local date_names = { ['en'] = { -- English ['long'] = {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12}, ['short'] = {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12}, ['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36}, ['season'] = {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23}, ['named'] = {['Easter'] = 98, ['Christmas'] = 99}, }, -- when local_date_names_from_mediawiki = false ['local'] = { -- replace these English date names with the local language equivalents ['long'] = {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12}, ['short'] = {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12}, ['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36}, ['season'] = {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23}, ['named'] = {['Easter'] = 98, ['Christmas'] = 99}, }, ['inv_local_long'] = {}, -- used in date reformatting & translation; copy of date_names['local'].long where k/v are inverted: [1]='<local name>' etc. ['inv_local_short'] = {}, -- used in date reformatting & translation; copy of date_names['local'].short where k/v are inverted: [1]='<local name>' etc. ['inv_local_quarter'] = {}, -- used in date translation; copy of date_names['local'].quarter where k/v are inverted: [1]='<local name>' etc. ['inv_local_season'] = {}, -- used in date translation; copy of date_names['local'].season where k/v are inverted: [1]='<local name>' etc. ['inv_local_named'] = {}, -- used in date translation; copy of date_names['local'].named where k/v are inverted: [1]='<local name>' etc. ['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'}, -- used to convert local language digits to Western 0-9 ['xlate_digits'] = {}, } if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled local long_t = {}; local short_t = {}; for i=1, 12 do -- loop 12x and local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i long_t[name] = i; -- save it name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i short_t[name] = i; -- save it end date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation end -- create inverted date-name tables for reformatting and/or translation for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd end end for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value end local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}} '{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count '{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k '{{ *[Uu]se +(MDY) +dates *[|}]', -- 788 '{{ *[Uu]se +(DMY) +dates *[|}]', -- 343 '{{ *([Mm]dy) *[|}]', -- 176 '{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18 '{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11 '{{ *([Dd]my) *[|}]', -- 56 '{{ *[Uu]se +(MDY) *[|}]', -- 5 '{{ *([Dd]MY) *[|}]', -- 3 '{{ *[Uu]se(mdy)dates *[|}]', -- 1 '{{ *[Uu]se +(DMY) *[|}]', -- 0 '{{ *([Mm]DY) *[|}]', -- 0 } local function get_date_format () local title_object = mw.title.getCurrentTitle(); if title_object.namespace == 10 then -- not in template space so that unused templates appear in unused-template-reports; return nil; -- auto-formatting does not work in Template space so don't set global_df end local content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625 for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format if match then content = content:match ('%b{}', start); -- get the whole template if content:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length return match:lower() .. '-' .. content:match ('| *cs1%-dates *= *([lsy][sy]?)'); else return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df= end end end end local global_df; --[[-----------------< V O L U M E , I S S U E , P A G E S >------------------ These tables hold cite class values (from the template invocation) and identify those templates that support |volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which is handled in the main module. ]] local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'} local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'} local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'} --[[ These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1 templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must not support |volume=. ]] local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used 'website', 'mailinglist', 'script-website', } local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used 'journal', 'magazine', 'newspaper', 'periodical', 'work', 'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work', } --[[ Patterns for finding extra text in |volume=, |issue=, |page=, |pages= ]] local vol_iss_pg_patterns = { good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not bad_ppatterns = { -- patterns for |page= and |pages= '^[Pp][PpGg]?%.?[ %d]', '^[Pp][Pp]?%.&nbsp;', -- from {{p.}} and {{pp.}} templates '^[Pp]ages?', '^[Pp]gs.?', }, vpatterns = { -- patterns for |volume= '^volumes?', '^vols?[%.:=]?' }, ipatterns = { -- patterns for |issue= '^issues?', '^iss[%.:=]?', '^numbers?', '^nos?%A', -- don't match 'november' or 'nostradamus' '^nr[%.:=]?', '^n[%.:= ]' -- might be a valid issue without separator (space char is sep char here) } } --[[--------------------------< K E Y W O R D S >------------------------------- These tables hold keywords for those parameters that have defined sets of acceptable keywords. ]] --[[-------------------< K E Y W O R D S T A B L E >-------------------------- this is a list of keywords; each key in the list is associated with a table of synonymous keywords possibly from different languages. for I18N: add local-language keywords to value table; do not change the key. For example, adding the German keyword 'ja': ['affirmative'] = {'yes', 'true', 'y', 'ja'}, Because CS1|2 templates from en.wiki articles are often copied to other local wikis, it is recommended that the English keywords remain in these tables. ]] local keywords = { ['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style= ['and'] = {'and', 'serial'}, -- |name-list-style= ['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot ['afterword'] = {'afterword'}, -- |contribution= ['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot ['cs1'] = {'cs1'}, -- |mode= ['cs2'] = {'cs2'}, -- |mode= ['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot ['dmy'] = {'dmy'}, -- |df= ['dmy-all'] = {'dmy-all'}, -- |df= ['foreword'] = {'foreword'}, -- |contribution= ['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot ['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true ['introduction'] = {'introduction'}, -- |contribution= ['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot ['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot ['mdy'] = {'mdy'}, -- |df= ['mdy-all'] = {'mdy-all'}, -- |df= ['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot ['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=) ['preface'] = {'preface'}, -- |contribution= ['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot ['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot ['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot ['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot ['vanc'] = {'vanc'}, -- |name-list-style= ['ymd'] = {'ymd'}, -- |df= ['ymd-all'] = {'ymd-all'}, -- |df= -- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki -- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki } --[[------------------------< X L A T E _ K E Y W O R D S >--------------------- this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{} become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}: ['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{} becomes ['yes'] = 'affirmative', -- in keywords_xlate{} ['true'] = 'affirmative', ['y'] = 'affirmative', the purpose of this function is to act as a translator between a non-English keyword and its English equivalent that may be used in other modules of this suite ]] local function xlate_keywords () local out_table = {}; -- output goes here for k, keywords_t in pairs (keywords) do -- spin through the keywords table for _, keyword in ipairs (keywords_t) do -- for each keyword out_table[keyword] = k; -- create an entry in the output table where keyword is the key end end return out_table; end local keywords_xlate = xlate_keywords (); -- the list of translated keywords --[[----------------< M A K E _ K E Y W O R D S _ L I S T >--------------------- this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter. keywords_lists{}, is a table of tables from keywords{} ]] local function make_keywords_list (keywords_lists) local out_table = {}; -- output goes here for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ... table.insert (out_table, keyword); -- ... as plain text, to the output list end end return out_table; end --[[----------------< K E Y W O R D S _ L I S T S >----------------------------- this is a list of lists of valid keywords for the various parameters in [key]. Generally the keys in this table are the canonical en.wiki parameter names though some are contrived because of use in multiple differently named parameters: ['yes_true_y'], ['id-access']. The function make_keywords_list() extracts the individual keywords from the appropriate list in keywords{}. The lists in this table are used to validate the keyword assignment for the parameters named in this table's keys. ]] local keywords_lists = { ['yes_true_y'] = make_keywords_list ({keywords.affirmative}), ['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}), ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}), -- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki ['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}), ['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}), ['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported ['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}), ['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}), ['id-access'] = make_keywords_list ({keywords.free}), } --[[---------------------< S T R I P M A R K E R S >---------------------------- Common pattern definition location for stripmarkers so that we don't have to go hunting for them if (when) MediaWiki changes their form. ]] local stripmarkers = { ['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker ['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker() } --[[------------< I N V I S I B L E _ C H A R A C T E R S >--------------------- This table holds non-printing or invisible characters indexed either by name or by Unicode group. Values are decimal representations of UTF-8 codes. The table is organized as a table of tables because the Lua pairs keyword returns table data in an arbitrary order. Here, we want to process the table from top to bottom because the entries at the top of the table are also found in the ranges specified by the entries at the bottom of the table. Also here is a pattern that recognizes stripmarkers that begin and end with the delete characters. The nowiki stripmarker is not an error but some others are because the parameter values that include them become part of the template's metadata before stripmarker replacement. ]] local invisible_defs = { del = '\127', -- used to distinguish between stripmarker and del char zwj = '\226\128\141', -- used with capture because zwj may be allowed } local invisible_chars = { {'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD {'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed {'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B {'hair space', '\226\128\138'}, -- U+200A, E2 80 8A {'soft hyphen', '\194\173'}, -- U+00AD, C2 AD {'horizontal tab', '\009'}, -- U+0009 (HT), 09 {'line feed', '\010'}, -- U+000A (LF), 0A {'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0 {'carriage return', '\013'}, -- U+000D (CR), 0D {'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type {'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker {'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D)) {'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F -- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF -- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF -- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD -- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD } --[[ Indic script makes use of zero width joiner as a character modifier so zwj characters must be left in. This pattern covers all of the unicode characters for these languages: Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf plus the not-necessarily Indic scripts for Sinhala and Burmese: Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf the pattern is used by has_invisible_chars() and coins_cleanup() ]] local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]'; -- list of emoji that use a zwj character (U+200D) to combine with another emoji -- from: https://unicode.org/Public/emoji/15.0/emoji-zwj-sequences.txt; version: 15.0; 2022-05-06 -- table created by: [[:en:Module:Make emoji zwj table]] local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx [9760] = true, -- U+2620 ☠ skull and crossbones [9792] = true, -- U+2640 ♀ female sign [9794] = true, -- U+2642 ♂ male sign [9877] = true, -- U+2695 ⚕ staff of aesculapius [9878] = true, -- U+2696 ⚖ scales [9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign [9992] = true, -- U+2708 ✈ airplane [10052] = true, -- U+2744 ❄ snowflake [10084] = true, -- U+2764 ❤ heavy black heart [11035] = true, -- U+2B1B ⬛ black large square [127752] = true, -- U+1F308 🌈 rainbow [127787] = true, -- U+1F32B 🌫 fog [127806] = true, -- U+1F33E 🌾 ear of rice [127859] = true, -- U+1F373 🍳 cooking [127868] = true, -- U+1F37C 🍼 baby bottle [127876] = true, -- U+1F384 🎄 christmas tree [127891] = true, -- U+1F393 🎓 graduation cap [127908] = true, -- U+1F3A4 🎤 microphone [127912] = true, -- U+1F3A8 🎨 artist palette [127979] = true, -- U+1F3EB 🏫 school [127981] = true, -- U+1F3ED 🏭 factory [128102] = true, -- U+1F466 👦 boy [128103] = true, -- U+1F467 👧 girl [128104] = true, -- U+1F468 👨 man [128105] = true, -- U+1F469 👩 woman [128139] = true, -- U+1F48B 💋 kiss mark [128168] = true, -- U+1F4A8 💨 dash symbol [128171] = true, -- U+1F4AB 💫 dizzy symbol [128187] = true, -- U+1F4BB 💻 personal computer [128188] = true, -- U+1F4BC 💼 brief case [128293] = true, -- U+1F525 🔥 fire [128295] = true, -- U+1F527 🔧 wrench [128300] = true, -- U+1F52C 🔬 microscope [128488] = true, -- U+1F5E8 🗨 left speech bubble [128640] = true, -- U+1F680 🚀 rocket [128658] = true, -- U+1F692 🚒 fire engine [129309] = true, -- U+1F91D 🤝 handshake [129455] = true, -- U+1F9AF 🦯 probing cane [129456] = true, -- U+1F9B0 🦰 emoji component red hair [129457] = true, -- U+1F9B1 🦱 emoji component curly hair [129458] = true, -- U+1F9B2 🦲 emoji component bald [129459] = true, -- U+1F9B3 🦳 emoji component white hair [129466] = true, -- U+1F9BA 🦺 safety vest [129468] = true, -- U+1F9BC 🦼 motorized wheelchair [129469] = true, -- U+1F9BD 🦽 manual wheelchair [129489] = true, -- U+1F9D1 🧑 adult [129657] = true, -- U+1FA79 🩹 adhesive bandage [129778] = true, -- U+1FAF2 🫲 leftwards hand } --[[----------------------< L A N G U A G E S U P P O R T >------------------- These tables and constants support various language-specific functionality. ]] --local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code if string.match (mw.site.server, 'wikidata') then this_wiki_code = mw.getCurrentFrame():preprocess('{{int:lang}}'); -- on Wikidata so use interface language setting instead end local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests local mw_languages_by_name_t = {}; for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language= v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>] if mw_languages_by_name_t[v] then -- when name already in the table if 2 == #k or 3 == #k then -- if tag does not have subtags mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name end else -- here when name not in the table mw_languages_by_name_t[v] = k; -- so add name and matching tag end end local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local) if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag inter_wiki_map[v["prefix"]] = true; -- add it to our local map end end --[[--------------------< S C R I P T _ L A N G _ C O D E S >------------------- This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character language codes that apply only to |script-title= and |script-chapter= ]] local script_lang_codes = { 'ab', 'am', 'ar', 'be', 'bg', 'bn', 'bo', 'bs', 'dv', 'dz', 'el', 'fa', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko', 'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mr', 'my', 'ne', 'or', 'ota', 'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt', 'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh' }; --[[---------------< L A N G U A G E R E M A P P I N G >---------------------- These tables hold language information that is different (correct) from MediaWiki's definitions For each ['code'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', 'code'} in lang_name_remap{} lang_code_remap{}: key is always lowercase ISO 639-1, -2, -3 language code or a valid lowercase IETF language tag value is properly spelled and capitalized language name associated with key only one language name per key; key/value pair must have matching entry in lang_name_remap{} lang_name_remap{}: key is always lowercase language name value is a table the holds correctly spelled and capitalized language name [1] and associated code [2] (code must match a code key in lang_code_remap{}) may have multiple keys referring to a common preferred name and code; For example: ['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh' ]] local lang_code_remap = { -- used for |language= and |script-title= / |script-chapter= ['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch ['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org ['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name ['bn'] = 'Bengali', -- MediaWiki returns Bangla ['ca-valencia'] = 'Valencian', -- IETF variant of Catalan ['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name ['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name ['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data ['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data ['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name } local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase ['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org ['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap ['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code ['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org ['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found ['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name ['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh ['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name ['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö) ['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name ['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639 ['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese ['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found ['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian } --[[---------------< P R O P E R T I E S _ C A T E G O R I E S >---------------- Properties categories. These are used for investigating qualities of citations. ]] local prop_cats = { ['foreign-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code ['foreign-lang-source-2'] = 'CS1 foreign language sources (ISO 639-2)|$1', -- |language= category; a cat for ISO639-2 languages; $1 is the ISO 639-2 code used as a sort key ['jul-greg-uncertainty'] = 'CS1: Julian–Gregorian uncertainty', -- probably temporary cat to identify scope of template with dates 1 October 1582 – 1 January 1926 ['local-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is local-language name, $2 is ISO639-1 code; not emitted when local_lang_cat_enable is false ['location-test'] = 'CS1 location test', ['long-vol'] = 'CS1: long volume value', -- probably temporary cat to identify scope of |volume= values longer than 4 characters ['script'] = 'CS1 uses $1-language script ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is ISO639-1 code ['tracked-param'] = 'CS1 tracked parameter: $1', -- $1 is base (enumerators removed) parameter name ['year-range-abbreviated'] = 'CS1: abbreviated year range', -- probably temporary cat to identify scope of |date=, |year= values using YYYY–YY form } --[[-------------------< T I T L E _ T Y P E S >-------------------------------- Here we map a template's CitationClass to TitleType (default values for |type= parameter) ]] local title_types = { ['AV-media-notes'] = 'Media notes', ['interview'] = 'Interview', ['mailinglist'] = 'Mailing list', ['map'] = 'Map', ['podcast'] = 'Podcast', ['pressrelease'] = 'Press release', ['report'] = 'Report', ['speech'] = 'Speech', ['techreport'] = 'Technical report', ['thesis'] = 'Thesis', } --[[===================<< E R R O R M E S S A G I N G >>====================== ]] --[[----------< E R R O R M E S S A G E S U P P L I M E N T S >------------- I18N for those messages that are supplemented with additional specific text that describes the reason for the error TODO: merge this with special_case_translations{}? ]] local err_msg_supl = { ['char'] = 'invalid character', -- |isbn=, |sbn= ['check'] = 'checksum', -- |isbn=, |sbn= ['flag'] = 'flag', -- |archive-url= ['form'] = 'invalid form', -- |isbn=, |sbn= ['group'] = 'invalid group id', -- |isbn= ['initials'] = 'initials', -- Vancouver ['invalid language code'] = 'invalid language code', -- |script-<param>= ['journal'] = 'journal', -- |bibcode= ['length'] = 'length', -- |isbn=, |bibcode=, |sbn= ['liveweb'] = 'liveweb', -- |archive-url= ['missing comma'] = 'missing comma', -- Vancouver ['missing prefix'] = 'missing prefix', -- |script-<param>= ['missing title part'] = 'missing title part', -- |script-<param>= ['name'] = 'name', -- Vancouver ['non-Latin char'] = 'non-Latin character', -- Vancouver ['path'] = 'path', -- |archive-url= ['prefix'] = 'invalid prefix', -- |isbn= ['punctuation'] = 'punctuation', -- Vancouver ['save'] = 'save command', -- |archive-url= ['suffix'] = 'suffix', -- Vancouver ['timestamp'] = 'timestamp', -- |archive-url= ['unknown language code'] = 'unknown language code', -- |script-<param>= ['value'] = 'value', -- |bibcode= ['year'] = 'year', -- |bibcode= } --[[--------------< E R R O R _ C O N D I T I O N S >--------------------------- Error condition table. This table has two sections: errors at the top, maintenance at the bottom. Maint 'messaging' does not have a 'message' (message=nil) The following contains a list of IDs for various error conditions defined in the code. For each ID, we specify a text message to display, an error category to include, and whether the error message should be wrapped as a hidden comment. Anchor changes require identical changes to matching anchor in Help:CS1 errors TODO: rename error_conditions{} to something more generic; create separate error and maint tables inside that? ]] local error_conditions = { err_accessdate_missing_url = { message = '<code class="cs1-code">&#124;access-date=</code> requires <code class="cs1-code">&#124;url=</code>', anchor = 'accessdate_missing_url', category = 'CS1 errors: access-date without URL', hidden = false }, err_apostrophe_markup = { message = 'Italic or bold markup not allowed in: <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'apostrophe_markup', category = 'CS1 errors: markup', hidden = false }, err_archive_missing_date = { message = '<code class="cs1-code">&#124;archive-url=</code> requires <code class="cs1-code">&#124;archive-date=</code>', anchor = 'archive_missing_date', category = 'CS1 errors: archive-url', hidden = false }, err_archive_missing_url = { message = '<code class="cs1-code">&#124;archive-url=</code> requires <code class="cs1-code">&#124;url=</code>', anchor = 'archive_missing_url', category = 'CS1 errors: archive-url', hidden = false }, err_archive_url = { message = '<code class="cs1-code">&#124;archive-url=</code> is malformed: $1', -- $1 is error message detail anchor = 'archive_url', category = 'CS1 errors: archive-url', hidden = false }, err_arxiv_missing = { message = '<code class="cs1-code">&#124;arxiv=</code> required', anchor = 'arxiv_missing', category = 'CS1 errors: arXiv', -- same as bad arxiv hidden = false }, err_asintld_missing_asin = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;asin=</code>', -- $1 is parameter name anchor = 'asintld_missing_asin', category = 'CS1 errors: ASIN TLD', hidden = false }, err_bad_arxiv = { message = 'Check <code class="cs1-code">&#124;arxiv=</code> value', anchor = 'bad_arxiv', category = 'CS1 errors: arXiv', hidden = false }, err_bad_asin = { message = 'Check <code class="cs1-code">&#124;asin=</code> value', anchor = 'bad_asin', category ='CS1 errors: ASIN', hidden = false }, err_bad_asin_tld = { message = 'Check <code class="cs1-code">&#124;asin-tld=</code> value', anchor = 'bad_asin_tld', category ='CS1 errors: ASIN TLD', hidden = false }, err_bad_bibcode = { message = 'Check <code class="cs1-code">&#124;bibcode=</code> $1', -- $1 is error message detail anchor = 'bad_bibcode', category = 'CS1 errors: bibcode', hidden = false }, err_bad_biorxiv = { message = 'Check <code class="cs1-code">&#124;biorxiv=</code> value', anchor = 'bad_biorxiv', category = 'CS1 errors: bioRxiv', hidden = false }, err_bad_citeseerx = { message = 'Check <code class="cs1-code">&#124;citeseerx=</code> value', anchor = 'bad_citeseerx', category = 'CS1 errors: citeseerx', hidden = false }, err_bad_date = { message = 'Check date values in: $1', -- $1 is a parameter name list anchor = 'bad_date', category = 'CS1 errors: dates', hidden = false }, err_bad_doi = { message = 'Check <code class="cs1-code">&#124;doi=</code> value', anchor = 'bad_doi', category = 'CS1 errors: DOI', hidden = false }, err_bad_hdl = { message = 'Check <code class="cs1-code">&#124;hdl=</code> value', anchor = 'bad_hdl', category = 'CS1 errors: HDL', hidden = false }, err_bad_isbn = { message = 'Check <code class="cs1-code">&#124;isbn=</code> value: $1', -- $1 is error message detail anchor = 'bad_isbn', category = 'CS1 errors: ISBN', hidden = false }, err_bad_ismn = { message = 'Check <code class="cs1-code">&#124;ismn=</code> value', anchor = 'bad_ismn', category = 'CS1 errors: ISMN', hidden = false }, err_bad_issn = { message = 'Check <code class="cs1-code">&#124;$1issn=</code> value', -- $1 is 'e' or '' for eissn or issn anchor = 'bad_issn', category = 'CS1 errors: ISSN', hidden = false }, err_bad_jfm = { message = 'Check <code class="cs1-code">&#124;jfm=</code> value', anchor = 'bad_jfm', category = 'CS1 errors: JFM', hidden = false }, err_bad_jstor = { message = 'Check <code class="cs1-code">&#124;jstor=</code> value', anchor = 'bad_jstor', category = 'CS1 errors: JSTOR', hidden = false }, err_bad_lccn = { message = 'Check <code class="cs1-code">&#124;lccn=</code> value', anchor = 'bad_lccn', category = 'CS1 errors: LCCN', hidden = false }, err_bad_mr = { message = 'Check <code class="cs1-code">&#124;mr=</code> value', anchor = 'bad_mr', category = 'CS1 errors: MR', hidden = false }, err_bad_oclc = { message = 'Check <code class="cs1-code">&#124;oclc=</code> value', anchor = 'bad_oclc', category = 'CS1 errors: OCLC', hidden = false }, err_bad_ol = { message = 'Check <code class="cs1-code">&#124;ol=</code> value', anchor = 'bad_ol', category = 'CS1 errors: OL', hidden = false }, err_bad_osti = { message = 'Check <code class="cs1-code">&#124;osti=</code> value', anchor = 'bad_osti', category = 'CS1 errors: OSTI', hidden = false }, err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link= message = 'Check <code class="cs1-code">&#124;$1=</code> value', -- $1 is parameter name anchor = 'bad_paramlink', category = 'CS1 errors: parameter link', hidden = false }, err_bad_pmc = { message = 'Check <code class="cs1-code">&#124;pmc=</code> value', anchor = 'bad_pmc', category = 'CS1 errors: PMC', hidden = false }, err_bad_pmid = { message = 'Check <code class="cs1-code">&#124;pmid=</code> value', anchor = 'bad_pmid', category = 'CS1 errors: PMID', hidden = false }, err_bad_rfc = { message = 'Check <code class="cs1-code">&#124;rfc=</code> value', anchor = 'bad_rfc', category = 'CS1 errors: RFC', hidden = false }, err_bad_s2cid = { message = 'Check <code class="cs1-code">&#124;s2cid=</code> value', anchor = 'bad_s2cid', category = 'CS1 errors: S2CID', hidden = false }, err_bad_sbn = { message = 'Check <code class="cs1-code">&#124;sbn=</code> value: $1', -- $1 is error message detail anchor = 'bad_sbn', category = 'CS1 errors: SBN', hidden = false }, err_bad_ssrn = { message = 'Check <code class="cs1-code">&#124;ssrn=</code> value', anchor = 'bad_ssrn', category = 'CS1 errors: SSRN', hidden = false }, err_bad_url = { message = 'Check $1 value', -- $1 is parameter name anchor = 'bad_url', category = 'CS1 errors: URL', hidden = false }, err_bad_usenet_id = { message = 'Check <code class="cs1-code">&#124;message-id=</code> value', anchor = 'bad_message_id', category = 'CS1 errors: message-id', hidden = false }, err_bad_zbl = { message = 'Check <code class="cs1-code">&#124;zbl=</code> value', anchor = 'bad_zbl', category = 'CS1 errors: Zbl', hidden = false }, err_bare_url_missing_title = { message = '$1 missing title', -- $1 is parameter name anchor = 'bare_url_missing_title', category = 'CS1 errors: bare URL', hidden = false }, err_biorxiv_missing = { message = '<code class="cs1-code">&#124;biorxiv=</code> required', anchor = 'biorxiv_missing', category = 'CS1 errors: bioRxiv', -- same as bad bioRxiv hidden = false }, err_chapter_ignored = { message = '<code class="cs1-code">&#124;$1=</code> ignored', -- $1 is parameter name anchor = 'chapter_ignored', category = 'CS1 errors: chapter ignored', hidden = false }, err_citation_missing_title = { message = 'Missing or empty <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'citation_missing_title', category = 'CS1 errors: missing title', hidden = false }, err_citeseerx_missing = { message = '<code class="cs1-code">&#124;citeseerx=</code> required', anchor = 'citeseerx_missing', category = 'CS1 errors: citeseerx', -- same as bad citeseerx hidden = false }, err_cite_web_url = { -- this error applies to cite web and to cite podcast message = 'Missing or empty <code class="cs1-code">&#124;url=</code>', anchor = 'cite_web_url', category = 'CS1 errors: requires URL', hidden = false }, err_class_ignored = { message = '<code class="cs1-code">&#124;class=</code> ignored', anchor = 'class_ignored', category = 'CS1 errors: class', hidden = false }, err_contributor_ignored = { message = '<code class="cs1-code">&#124;contributor=</code> ignored', anchor = 'contributor_ignored', category = 'CS1 errors: contributor', hidden = false }, err_contributor_missing_required_param = { message = '<code class="cs1-code">&#124;contributor=</code> requires <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'contributor_missing_required_param', category = 'CS1 errors: contributor', hidden = false }, err_deprecated_params = { message = 'Cite uses deprecated parameter <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'deprecated_params', category = 'CS1 errors: deprecated parameters', hidden = false }, err_disp_name = { message = 'Invalid <code class="cs1-code">&#124;$1=$2</code>', -- $1 is parameter name; $2 is the assigned value anchor = 'disp_name', category = 'CS1 errors: display-names', hidden = false, }, err_doibroken_missing_doi = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;doi=</code>', -- $1 is parameter name anchor = 'doibroken_missing_doi', category = 'CS1 errors: DOI', hidden = false }, err_embargo_missing_pmc = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;pmc=</code>', -- $1 is parameter name anchor = 'embargo_missing_pmc', category = 'CS1 errors: PMC embargo', hidden = false }, err_empty_citation = { message = 'Empty citation', anchor = 'empty_citation', category = 'CS1 errors: empty citation', hidden = false }, err_etal = { message = 'Explicit use of et al. in: <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'explicit_et_al', category = 'CS1 errors: explicit use of et al.', hidden = false }, err_extra_text_edition = { message = '<code class="cs1-code">&#124;edition=</code> has extra text', anchor = 'extra_text_edition', category = 'CS1 errors: extra text: edition', hidden = false, }, err_extra_text_issue = { message = '<code class="cs1-code">&#124;$1=</code> has extra text', -- $1 is parameter name anchor = 'extra_text_issue', category = 'CS1 errors: extra text: issue', hidden = false, }, err_extra_text_pages = { message = '<code class="cs1-code">&#124;$1=</code> has extra text', -- $1 is parameter name anchor = 'extra_text_pages', category = 'CS1 errors: extra text: pages', hidden = false, }, err_extra_text_volume = { message = '<code class="cs1-code">&#124;$1=</code> has extra text', -- $1 is parameter name anchor = 'extra_text_volume', category = 'CS1 errors: extra text: volume', hidden = true, }, err_first_missing_last = { message = '<code class="cs1-code">&#124;$1=</code> missing <code class="cs1-code">&#124;$2=</code>', -- $1 is first alias, $2 is matching last alias anchor = 'first_missing_last', category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator hidden = false }, err_format_missing_url = { message = '<code class="cs1-code">&#124;$1=</code> requires <code class="cs1-code">&#124;$2=</code>', -- $1 is format parameter $2 is url parameter anchor = 'format_missing_url', category = 'CS1 errors: format without URL', hidden = false }, err_generic_name = { message = '<code class="cs1-code">&#124;$1=</code> has generic name', -- $1 is parameter name anchor = 'generic_name', category = 'CS1 errors: generic name', hidden = false, }, err_generic_title = { message = 'Cite uses generic title', anchor = 'generic_title', category = 'CS1 errors: generic title', hidden = false, }, err_invalid_param_val = { message = 'Invalid <code class="cs1-code">&#124;$1=$2</code>', -- $1 is parameter name $2 is parameter value anchor = 'invalid_param_val', category = 'CS1 errors: invalid parameter value', hidden = false }, err_invisible_char = { message = '$1 in $2 at position $3', -- $1 is invisible char $2 is parameter name $3 is position number anchor = 'invisible_char', category = 'CS1 errors: invisible characters', hidden = false }, err_missing_name = { message = 'Missing <code class="cs1-code">&#124;$1$2=</code>', -- $1 is modified NameList; $2 is enumerator anchor = 'missing_name', category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator hidden = false }, err_missing_periodical = { message = 'Cite $1 requires <code class="cs1-code">&#124;$2=</code>', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1 anchor = 'missing_periodical', category = 'CS1 errors: missing periodical', hidden = true }, err_missing_pipe = { message = 'Missing pipe in: <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'missing_pipe', category = 'CS1 errors: missing pipe', hidden = false }, err_param_access_requires_param = { message = '<code class="cs1-code">&#124;$1-access=</code> requires <code class="cs1-code">&#124;$1=</code>', -- $1 is parameter name anchor = 'param_access_requires_param', category = 'CS1 errors: param-access', hidden = false }, err_param_has_ext_link = { message = 'External link in <code class="cs1-code">$1</code>', -- $1 is parameter name anchor = 'param_has_ext_link', category = 'CS1 errors: external links', hidden = false }, err_parameter_ignored = { message = 'Unknown parameter <code class="cs1-code">&#124;$1=</code> ignored', -- $1 is parameter name anchor = 'parameter_ignored', category = 'CS1 errors: unsupported parameter', hidden = false }, err_parameter_ignored_suggest = { message = 'Unknown parameter <code class="cs1-code">&#124;$1=</code> ignored (<code class="cs1-code">&#124;$2=</code> suggested)', -- $1 is unknown parameter $2 is suggested parameter name anchor = 'parameter_ignored_suggest', category = 'CS1 errors: unsupported parameter', hidden = false }, err_redundant_parameters = { message = 'More than one of $1 specified', -- $1 is error message detail anchor = 'redundant_parameters', category = 'CS1 errors: redundant parameter', hidden = false }, err_script_parameter = { message = 'Invalid <code class="cs1-code">&#124;$1=</code>: $2', -- $1 is parameter name $2 is script language code or error detail anchor = 'script_parameter', category = 'CS1 errors: script parameters', hidden = false }, err_ssrn_missing = { message = '<code class="cs1-code">&#124;ssrn=</code> required', anchor = 'ssrn_missing', category = 'CS1 errors: SSRN', -- same as bad arxiv hidden = false }, err_text_ignored = { message = 'Text "$1" ignored', -- $1 is ignored text anchor = 'text_ignored', category = 'CS1 errors: unrecognized parameter', hidden = false }, err_trans_missing_title = { message = '<code class="cs1-code">&#124;trans-$1=</code> requires <code class="cs1-code">&#124;$1=</code> or <code class="cs1-code">&#124;script-$1=</code>', -- $1 is base parameter name anchor = 'trans_missing_title', category = 'CS1 errors: translated title', hidden = false }, err_param_unknown_empty = { message = 'Cite has empty unknown parameter$1: $2', -- $1 is 's' or empty space; $2 is empty unknown param list anchor = 'param_unknown_empty', category = 'CS1 errors: empty unknown parameters', hidden = false }, err_vancouver = { message = 'Vancouver style error: $1 in name $2', -- $1 is error detail, $2 is the nth name anchor = 'vancouver', category = 'CS1 errors: Vancouver style', hidden = false }, err_wikilink_in_url = { message = 'URL–wikilink conflict', -- uses ndash anchor = 'wikilink_in_url', category = 'CS1 errors: URL–wikilink conflict', -- uses ndash hidden = false }, --[[--------------------------< M A I N T >------------------------------------- maint messages do not have a message (message = nil); otherwise the structure is the same as error messages ]] maint_archived_copy = { message = nil, anchor = 'archived_copy', category = 'CS1 maint: archived copy as title', hidden = true, }, maint_authors = { message = nil, anchor = 'authors', category = 'CS1 maint: uses authors parameter', hidden = true, }, maint_bot_unknown = { message = nil, anchor = 'bot:_unknown', category = 'CS1 maint: bot: original URL status unknown', hidden = true, }, maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki message = nil, anchor = 'date_auto_xlated', category = 'CS1 maint: date auto-translated', hidden = true, }, maint_date_format = { message = nil, anchor = 'date_format', category = 'CS1 maint: date format', hidden = true, }, maint_date_year = { message = nil, anchor = 'date_year', category = 'CS1 maint: date and year', hidden = true, }, maint_doi_ignore = { message = nil, anchor = 'doi_ignore', category = 'CS1 maint: ignored DOI errors', hidden = true, }, maint_doi_inactive = { message = nil, anchor = 'doi_inactive', category = 'CS1 maint: DOI inactive', hidden = true, }, maint_doi_inactive_dated = { message = nil, anchor = 'doi_inactive_dated', category = 'CS1 maint: DOI inactive as of $2$3$1', -- $1 is year, $2 is month-name or empty string, $3 is space or empty string hidden = true, }, maint_extra_punct = { message = nil, anchor = 'extra_punct', category = 'CS1 maint: extra punctuation', hidden = true, }, maint_isbn_ignore = { message = nil, anchor = 'ignore_isbn_err', category = 'CS1 maint: ignored ISBN errors', hidden = true, }, maint_issn_ignore = { message = nil, anchor = 'ignore_issn', category = 'CS1 maint: ignored ISSN errors', hidden = true, }, maint_jfm_format = { message = nil, anchor = 'jfm_format', category = 'CS1 maint: JFM format', hidden = true, }, maint_location = { message = nil, anchor = 'location', category = 'CS1 maint: location', hidden = true, }, maint_mr_format = { message = nil, anchor = 'mr_format', category = 'CS1 maint: MR format', hidden = true, }, maint_mult_names = { message = nil, anchor = 'mult_names', category = 'CS1 maint: multiple names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table hidden = true, }, maint_numeric_names = { message = nil, anchor = 'numeric_names', category = 'CS1 maint: numeric names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table hidden = true, }, maint_others = { message = nil, anchor = 'others', category = 'CS1 maint: others', hidden = true, }, maint_others_avm = { message = nil, anchor = 'others_avm', category = 'CS1 maint: others in cite AV media (notes)', hidden = true, }, maint_pmc_embargo = { message = nil, anchor = 'embargo', category = 'CS1 maint: PMC embargo expired', hidden = true, }, maint_pmc_format = { message = nil, anchor = 'pmc_format', category = 'CS1 maint: PMC format', hidden = true, }, maint_postscript = { message = nil, anchor = 'postscript', category = 'CS1 maint: postscript', hidden = true, }, maint_ref_duplicates_default = { message = nil, anchor = 'ref_default', category = 'CS1 maint: ref duplicates default', hidden = true, }, maint_unfit = { message = nil, anchor = 'unfit', category = 'CS1 maint: unfit URL', hidden = true, }, maint_unknown_lang = { message = nil, anchor = 'unknown_lang', category = 'CS1 maint: unrecognized language', hidden = true, }, maint_untitled = { message = nil, anchor = 'untitled', category = 'CS1 maint: untitled periodical', hidden = true, }, maint_url_status = { message = nil, anchor = 'url_status', category = 'CS1 maint: url-status', hidden = true, }, maint_zbl = { message = nil, anchor = 'zbl', category = 'CS1 maint: Zbl', hidden = true, }, } --[[--------------------------< I D _ H A N D L E R S >-------------------------------------------------------- The following contains a list of values for various defined identifiers. For each identifier we specify a variety of information necessary to properly render the identifier in the citation. parameters: a list of parameter aliases for this identifier; first in the list is the canonical form link: Wikipedia article name redirect: a local redirect to a local Wikipedia article name; at en.wiki, 'ISBN (identifier)' is a redirect to 'International Standard Book Number' q: Wikidata q number for the identifier label: the label preceding the identifier; label is linked to a Wikipedia article (in this order): redirect from id_handlers['<id>'].redirect when use_identifier_redirects is true Wikidata-supplied article name for the local wiki from id_handlers['<id>'].q local article name from id_handlers['<id>'].link prefix: the first part of a URL that will be concatenated with a second part which usually contains the identifier suffix: optional third part to be added after the identifier encode: true if URI should be percent-encoded; otherwise false COinS: identifier link or keyword for use in COinS: for identifiers registered at info-uri.info use: info:.... where '...' is the appropriate identifier label for identifiers that have COinS keywords, use the keyword: rft.isbn, rft.issn, rft.eissn for |asin= and |ol=, which require assembly, use the keyword: url for others make a URL using the value in prefix/suffix and #label, use the keyword: pre (not checked; any text other than 'info', 'rft', or 'url' works here) set to nil to leave the identifier out of the COinS separator: character or text between label and the identifier in the rendered citation id_limit: for those identifiers with established limits, this property holds the upper limit access: use this parameter to set the access level for all instances of this identifier. the value must be a valid access level for an identifier (see ['id-access'] in this file). custom_access: to enable custom access level for an identifier, set this parameter to the parameter that should control it (normally 'id-access') ]] local id_handlers = { ['ARXIV'] = { parameters = {'arxiv', 'eprint'}, link = 'arXiv', redirect = 'arXiv (identifier)', q = 'Q118398', label = 'arXiv', prefix = 'https://arxiv.org/abs/', -- protocol-relative tested 2013-09-04 encode = false, COinS = 'info:arxiv', separator = ':', access = 'free', -- free to read }, ['ASIN'] = { parameters = { 'asin', 'ASIN' }, link = 'Amazon Standard Identification Number', redirect = 'ASIN (identifier)', q = 'Q1753278', label = 'ASIN', prefix = 'https://www.amazon.', COinS = 'url', separator = '&nbsp;', encode = false; }, ['BIBCODE'] = { parameters = {'bibcode'}, link = 'Bibcode', redirect = 'Bibcode (identifier)', q = 'Q25754', label = 'Bibcode', prefix = 'https://ui.adsabs.harvard.edu/abs/', encode = false, COinS = 'info:bibcode', separator = ':', custom_access = 'bibcode-access', }, ['BIORXIV'] = { parameters = {'biorxiv'}, link = 'bioRxiv', redirect = 'bioRxiv (identifier)', q = 'Q19835482', label = 'bioRxiv', prefix = 'https://doi.org/', COinS = 'pre', -- use prefix value access = 'free', -- free to read encode = true, separator = '&nbsp;', }, ['CITESEERX'] = { parameters = {'citeseerx'}, link = 'CiteSeerX', redirect = 'CiteSeerX (identifier)', q = 'Q2715061', label = 'CiteSeerX', prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=', COinS = 'pre', -- use prefix value access = 'free', -- free to read encode = true, separator = '&nbsp;', }, ['DOI'] = { -- Used by InternetArchiveBot parameters = { 'doi', 'DOI'}, link = 'Digital object identifier', redirect = 'doi (identifier)', q = 'Q25670', label = 'doi', prefix = 'https://doi.org/', COinS = 'info:doi', separator = ':', encode = true, custom_access = 'doi-access', }, ['EISSN'] = { parameters = {'eissn', 'EISSN'}, link = 'International Standard Serial Number#Electronic ISSN', redirect = 'eISSN (identifier)', q = 'Q46339674', label = 'eISSN', prefix = 'https://www.worldcat.org/issn/', COinS = 'rft.eissn', encode = false, separator = '&nbsp;', }, ['HDL'] = { parameters = { 'hdl', 'HDL' }, link = 'Handle System', redirect = 'hdl (identifier)', q = 'Q3126718', label = 'hdl', prefix = 'https://hdl.handle.net/', COinS = 'info:hdl', separator = ':', encode = true, custom_access = 'hdl-access', }, ['ISBN'] = { -- Used by InternetArchiveBot parameters = {'isbn', 'ISBN'}, link = 'International Standard Book Number', redirect = 'ISBN (identifier)', q = 'Q33057', label = 'ISBN', prefix = 'Special:BookSources/', COinS = 'rft.isbn', separator = '&nbsp;', }, ['ISMN'] = { parameters = {'ismn', 'ISMN'}, link = 'International Standard Music Number', redirect = 'ISMN (identifier)', q = 'Q1666938', label = 'ISMN', prefix = '', -- not currently used; COinS = nil, -- nil because we can't use pre or rft or info: separator = '&nbsp;', }, ['ISSN'] = { parameters = {'issn', 'ISSN'}, link = 'International Standard Serial Number', redirect = 'ISSN (identifier)', q = 'Q131276', label = 'ISSN', prefix = 'https://www.worldcat.org/issn/', COinS = 'rft.issn', encode = false, separator = '&nbsp;', }, ['JFM'] = { parameters = {'jfm', 'JFM'}, link = 'Jahrbuch über die Fortschritte der Mathematik', redirect = 'JFM (identifier)', q = '', label = 'JFM', prefix = 'https://zbmath.org/?format=complete&q=an:', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', }, ['JSTOR'] = { parameters = {'jstor', 'JSTOR'}, link = 'JSTOR', redirect = 'JSTOR (identifier)', q = 'Q1420342', label = 'JSTOR', prefix = 'https://www.jstor.org/stable/', -- protocol-relative tested 2013-09-04 COinS = 'pre', -- use prefix value encode = false, separator = '&nbsp;', custom_access = 'jstor-access', }, ['LCCN'] = { parameters = {'lccn', 'LCCN'}, link = 'Library of Congress Control Number', redirect = 'LCCN (identifier)', q = 'Q620946', label = 'LCCN', prefix = 'https://lccn.loc.gov/', -- protocol-relative tested 2015-12-28 COinS = 'info:lccn', encode = false, separator = '&nbsp;', }, ['MR'] = { parameters = {'mr', 'MR'}, link = 'Mathematical Reviews', redirect = 'MR (identifier)', q = 'Q211172', label = 'MR', prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', }, ['OCLC'] = { parameters = {'oclc', 'OCLC'}, link = 'OCLC', redirect = 'OCLC (identifier)', q = 'Q190593', label = 'OCLC', prefix = 'https://www.worldcat.org/oclc/', COinS = 'info:oclcnum', encode = true, separator = '&nbsp;', id_limit = 9999999999, -- 10-digits }, ['OL'] = { parameters = { 'ol', 'OL' }, link = 'Open Library', redirect = 'OL (identifier)', q = 'Q1201876', label = 'OL', prefix = 'https://openlibrary.org/', COinS = 'url', separator = '&nbsp;', encode = true, custom_access = 'ol-access', }, ['OSTI'] = { parameters = {'osti', 'OSTI'}, link = 'Office of Scientific and Technical Information', redirect = 'OSTI (identifier)', q = 'Q2015776', label = 'OSTI', prefix = 'https://www.osti.gov/biblio/', -- protocol-relative tested 2018-09-12 COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', id_limit = 23010000, custom_access = 'osti-access', }, ['PMC'] = { parameters = {'pmc', 'PMC'}, link = 'PubMed Central', redirect = 'PMC (identifier)', q = 'Q229883', label = 'PMC', prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC', suffix = '', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', id_limit = 10500000, access = 'free', -- free to read }, ['PMID'] = { parameters = {'pmid', 'PMID'}, link = 'PubMed Identifier', redirect = 'PMID (identifier)', q = 'Q2082879', label = 'PMID', prefix = 'https://pubmed.ncbi.nlm.nih.gov/', COinS = 'info:pmid', encode = false, separator = '&nbsp;', id_limit = 37900000, }, ['RFC'] = { parameters = {'rfc', 'RFC'}, link = 'Request for Comments', redirect = 'RFC (identifier)', q = 'Q212971', label = 'RFC', prefix = 'https://tools.ietf.org/html/rfc', COinS = 'pre', -- use prefix value encode = false, separator = '&nbsp;', id_limit = 9300, access = 'free', -- free to read }, ['SBN'] = { parameters = {'sbn', 'SBN'}, link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History redirect = 'SBN (identifier)', label = 'SBN', prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn COinS = nil, -- nil because we can't use pre or rft or info: separator = '&nbsp;', }, ['SSRN'] = { parameters = {'ssrn', 'SSRN'}, link = 'Social Science Research Network', redirect = 'SSRN (identifier)', q = 'Q7550801', label = 'SSRN', prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', id_limit = 4500000, custom_access = 'ssrn-access', }, ['S2CID'] = { parameters = {'s2cid', 'S2CID'}, link = 'Semantic Scholar', redirect = 'S2CID (identifier)', q = 'Q22908627', label = 'S2CID', prefix = 'https://api.semanticscholar.org/CorpusID:', COinS = 'pre', -- use prefix value encode = false, separator = '&nbsp;', id_limit = 260000000, custom_access = 's2cid-access', }, ['USENETID'] = { parameters = {'message-id'}, link = 'Usenet', redirect = 'Usenet (identifier)', q = 'Q193162', label = 'Usenet:', prefix = 'news:', encode = false, COinS = 'pre', -- use prefix value separator = '&nbsp;', }, ['ZBL'] = { parameters = {'zbl', 'ZBL' }, link = 'Zentralblatt MATH', redirect = 'Zbl (identifier)', q = 'Q190269', label = 'Zbl', prefix = 'https://zbmath.org/?format=complete&q=an:', COinS = 'pre', -- use prefix value encode = true, separator = '&nbsp;', }, } --[[--------------------------< E X P O R T S >--------------------------------- ]] return { use_identifier_redirects = true, -- when true use redirect name for identifier label links; always true at en.wiki local_lang_cat_enable = false; -- when true categorizes pages where |language=<local wiki's language>; always false at en.wiki date_name_auto_xlate_enable = false; -- when true translates English month-names to the local-wiki's language month names; always false at en.wiki date_digit_auto_xlate_enable = false; -- when true translates Western date digit to the local-wiki's language digits (date_names['local_digits']); always false at en.wiki -- tables and variables created when this module is loaded global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format. punct_skip = build_skip_table (punct_skip, punct_meta_params), url_skip = build_skip_table (url_skip, url_meta_params), aliases = aliases, special_case_translation = special_case_translation, date_names = date_names, err_msg_supl = err_msg_supl, error_conditions = error_conditions, editor_markup_patterns = editor_markup_patterns, et_al_patterns = et_al_patterns, id_handlers = id_handlers, keywords_lists = keywords_lists, keywords_xlate = keywords_xlate, stripmarkers = stripmarkers, invisible_chars = invisible_chars, invisible_defs = invisible_defs, indic_script = indic_script, emoji_t = emoji_t, maint_cats = maint_cats, messages = messages, presentation = presentation, prop_cats = prop_cats, script_lang_codes = script_lang_codes, lang_code_remap = lang_code_remap, lang_name_remap = lang_name_remap, this_wiki_code = this_wiki_code, title_types = title_types, uncategorized_namespaces = uncategorized_namespaces_t, uncategorized_subpages = uncategorized_subpages, templates_using_volume = templates_using_volume, templates_using_issue = templates_using_issue, templates_not_using_page = templates_not_using_page, vol_iss_pg_patterns = vol_iss_pg_patterns, single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t, inter_wiki_map = inter_wiki_map, mw_languages_by_tag_t = mw_languages_by_tag_t, mw_languages_by_name_t = mw_languages_by_name_t, citation_class_map_t = citation_class_map_t, citation_issue_t = citation_issue_t, citation_no_volume_t = citation_no_volume_t, } ca84924af6925d67aa96e5d4f583c17bd12f69a1 Module:Citation/CS1/Whitelist 828 398 797 796 2023-07-10T15:57:46Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1/Whitelist]] Scribunto text/plain --[[--------------------------< S U P P O R T E D P A R A M E T E R S >-------------------------------------- Because a steady-state signal conveys no useful information, whitelist.basic_arguments[] list items can have three values: true - these parameters are valid and supported parameters false - these parameters are deprecated but still supported tracked - these parameters are valid and supported parameters tracked in an eponymous properties category nil - these parameters are no longer supported. remove entirely ]] local basic_arguments = { ['accessdate'] = true, ['access-date'] = true, ['agency'] = true, ['archivedate'] = true, ['archive-date'] = true, ['archive-format'] = true, ['archiveurl'] = true, ['archive-url'] = true, ['article'] = true, ['article-format'] = true, ['article-number'] = true, -- {{cite journal}}, {{cite conference}}; {{citation}} when |journal= has a value ['article-url'] = true, ['article-url-access'] = true, ['arxiv'] = true, -- cite arxiv; here because allowed in cite ... as identifier ['asin'] = true, ['ASIN'] = true, ['asin-tld'] = true, ['at'] = true, ['author'] = true, ['author-first'] = true, ['author-given'] = true, ['author-last'] = true, ['author-surname'] = true, ['authorlink'] = true, ['author-link'] = true, ['author-mask'] = true, ['authors'] = true, ['bibcode'] = true, ['bibcode-access'] = true, ['biorxiv'] = true, -- cite biorxiv; here because allowed in cite ... as identifier ['chapter'] = true, ['chapter-format'] = true, ['chapter-url'] = true, ['chapter-url-access'] = true, ['citeseerx'] = true, -- cite citeseerx; here because allowed in cite ... as identifier ['collaboration'] = true, ['contribution'] = true, ['contribution-format'] = true, ['contribution-url'] = true, ['contribution-url-access'] = true, ['contributor'] = true, ['contributor-first'] = true, ['contributor-given'] = true, ['contributor-last'] = true, ['contributor-surname'] = true, ['contributor-link'] = true, ['contributor-mask'] = true, ['date'] = true, ['department'] = true, ['df'] = true, ['dictionary'] = true, ['display-authors'] = true, ['display-contributors'] = true, ['display-editors'] = true, ['display-interviewers'] = true, ['display-subjects'] = true, ['display-translators'] = true, ['doi'] = true, ['DOI'] = true, ['doi-access'] = true, ['doi-broken-date'] = true, ['edition'] = true, ['editor'] = true, ['editor-first'] = true, ['editor-given'] = true, ['editor-last'] = true, ['editor-surname'] = true, ['editor-link'] = true, ['editor-mask'] = true, ['eissn'] = true, ['EISSN'] = true, ['encyclopaedia'] = true, ['encyclopedia'] = true, ['entry'] = true, ['entry-format'] = true, ['entry-url'] = true, ['entry-url-access'] = true, ['eprint'] = true, -- cite arxiv; here because allowed in cite ... as identifier ['first'] = true, ['format'] = true, ['given'] = true, ['hdl'] = true, ['HDL'] = true, ['hdl-access'] = true, ['host'] = true, -- unique to certain templates? ['id'] = true, ['ID'] = true, ['institution'] = true, -- constrain to cite thesis? ['interviewer'] = true, ['interviewer-first'] = true, ['interviewer-given'] = true, ['interviewer-last'] = true, ['interviewer-surname'] = true, ['interviewer-link'] = true, ['interviewer-mask'] = true, ['isbn'] = true, ['ISBN'] = true, ['ismn'] = true, ['ISMN'] = true, ['issn'] = true, ['ISSN'] = true, ['issue'] = true, ['jfm'] = true, ['JFM'] = true, ['journal'] = true, ['jstor'] = true, ['JSTOR'] = true, ['jstor-access'] = true, ['lang'] = true, ['language'] = true, ['last'] = true, ['lay-date'] = false, ['lay-format'] = false, ['lay-source'] = false, ['lay-url'] = false, ['lccn'] = true, ['LCCN'] = true, ['location'] = true, ['magazine'] = true, ['medium'] = true, ['minutes'] = true, -- constrain to cite AV media and podcast? ['mode'] = true, ['mr'] = true, ['MR'] = true, ['name-list-style'] = true, ['newspaper'] = true, ['no-pp'] = true, ['no-tracking'] = true, ['number'] = true, ['oclc'] = true, ['OCLC'] = true, ['ol'] = true, ['OL'] = true, ['ol-access'] = true, ['orig-date'] = true, ['origyear'] = true, ['orig-year'] = true, ['osti'] = true, ['OSTI'] = true, ['osti-access'] = true, ['others'] = true, ['p'] = true, ['page'] = true, ['pages'] = true, ['people'] = true, ['periodical'] = true, ['place'] = true, ['pmc'] = true, ['PMC'] = true, ['pmc-embargo-date'] = true, ['pmid'] = true, ['PMID'] = true, ['postscript'] = true, ['pp'] = true, ['publication-date'] = true, ['publication-place'] = true, ['publisher'] = true, ['quotation'] = true, ['quote'] = true, ['quote-page'] = true, ['quote-pages'] = true, ['ref'] = true, ['rfc'] = true, ['RFC'] = true, ['sbn'] = true, ['SBN'] = true, ['scale'] = true, ['script-article'] = true, ['script-chapter'] = true, ['script-contribution'] = true, ['script-entry'] = true, ['script-journal'] = true, ['script-magazine'] = true, ['script-newspaper'] = true, ['script-periodical'] = true, ['script-quote'] = true, ['script-section'] = true, ['script-title'] = true, ['script-website'] = true, ['script-work'] = true, ['section'] = true, ['section-format'] = true, ['section-url'] = true, ['section-url-access'] = true, ['series'] = true, ['ssrn'] = true, -- cite ssrn; these three here because allowed in cite ... as identifier ['SSRN'] = true, ['ssrn-access'] = true, ['subject'] = true, ['subject-link'] = true, ['subject-mask'] = true, ['surname'] = true, ['s2cid'] = true, ['S2CID'] = true, ['s2cid-access'] = true, ['template-doc-demo'] = true, ['time'] = true, -- constrain to cite av media and podcast? ['time-caption'] = true, -- constrain to cite av media and podcast? ['title'] = true, ['title-link'] = true, ['translator'] = true, ['translator-first'] = true, ['translator-given'] = true, ['translator-last'] = true, ['translator-surname'] = true, ['translator-link'] = true, ['translator-mask'] = true, ['trans-article'] = true, ['trans-chapter'] = true, ['trans-contribution'] = true, ['trans-entry'] = true, ['trans-journal'] = true, ['trans-magazine'] = true, ['trans-newspaper'] = true, ['trans-periodical'] = true, ['trans-quote'] = true, ['trans-section'] = true, ['trans-title'] = true, ['trans-website'] = true, ['trans-work'] = true, ['type'] = true, ['url'] = true, ['URL'] = true, ['url-access'] = true, ['url-status'] = true, ['vauthors'] = true, ['veditors'] = true, ['version'] = true, ['via'] = true, ['volume'] = true, ['website'] = true, ['work'] = true, ['year'] = true, ['zbl'] = true, ['ZBL'] = true, } local numbered_arguments = { ['author#'] = true, ['author-first#'] = true, ['author#-first'] = true, ['author-given#'] = true, ['author#-given'] = true, ['author-last#'] = true, ['author#-last'] = true, ['author-surname#'] = true, ['author#-surname'] = true, ['author-link#'] = true, ['author#-link'] = true, ['authorlink#'] = true, ['author#link'] = true, ['author-mask#'] = true, ['author#-mask'] = true, ['contributor#'] = true, ['contributor-first#'] = true, ['contributor#-first'] = true, ['contributor-given#'] = true, ['contributor#-given'] = true, ['contributor-last#'] = true, ['contributor#-last'] = true, ['contributor-surname#'] = true, ['contributor#-surname'] = true, ['contributor-link#'] = true, ['contributor#-link'] = true, ['contributor-mask#'] = true, ['contributor#-mask'] = true, ['editor#'] = true, ['editor-first#'] = true, ['editor#-first'] = true, ['editor-given#'] = true, ['editor#-given'] = true, ['editor-last#'] = true, ['editor#-last'] = true, ['editor-surname#'] = true, ['editor#-surname'] = true, ['editor-link#'] = true, ['editor#-link'] = true, ['editor-mask#'] = true, ['editor#-mask'] = true, ['first#'] = true, ['given#'] = true, ['host#'] = true, ['interviewer#'] = true, ['interviewer-first#'] = true, ['interviewer#-first'] = true, ['interviewer-given#'] = true, ['interviewer#-given'] = true, ['interviewer-last#'] = true, ['interviewer#-last'] = true, ['interviewer-surname#'] = true, ['interviewer#-surname'] = true, ['interviewer-link#'] = true, ['interviewer#-link'] = true, ['interviewer-mask#'] = true, ['interviewer#-mask'] = true, ['last#'] = true, ['subject#'] = true, ['subject-link#'] = true, ['subject#-link'] = true, ['subject-mask#'] = true, ['subject#-mask'] = true, ['surname#'] = true, ['translator#'] = true, ['translator-first#'] = true, ['translator#-first'] = true, ['translator-given#'] = true, ['translator#-given'] = true, ['translator-last#'] = true, ['translator#-last'] = true, ['translator-surname#'] = true, ['translator#-surname'] = true, ['translator-link#'] = true, ['translator#-link'] = true, ['translator-mask#'] = true, ['translator#-mask'] = true, } --[[--------------------------< P R E P R I N T S U P P O R T E D P A R A M E T E R S >-------------------- Cite arXiv, cite biorxiv, cite citeseerx, and cite ssrn are preprint templates that use the limited set of parameters defined in the limited_basic_arguments and limited_numbered_arguments tables. Those lists are supplemented with a template-specific list of parameters that are required by the particular template and may be exclusive to one of the preprint templates. Some of these parameters may also be available to the general cs1|2 templates. Same conventions for true/false/tracked/nil as above. ]] local preprint_arguments = { arxiv = { ['arxiv'] = true, -- cite arxiv and arxiv identifiers ['class'] = true, ['eprint'] = true, -- cite arxiv and arxiv identifiers }, biorxiv = { ['biorxiv'] = true, }, citeseerx = { ['citeseerx'] = true, }, ssrn = { ['ssrn'] = true, ['SSRN'] = true, ['ssrn-access'] = true, }, } --[[--------------------------< L I M I T E D S U P P O R T E D P A R A M E T E R S >---------------------- cite arxiv, cite biorxiv, cite citeseerx, and cite ssrn templates are preprint templates so are allowed only a limited subset of parameters allowed to all other cs1|2 templates. The limited subset is defined here. Same conventions for true/false/tracked/nil as above. ]] local limited_basic_arguments = { ['at'] = true, ['author'] = true, ['author-first'] = true, ['author-given'] = true, ['author-last'] = true, ['author-surname'] = true, ['author-link'] = true, ['authorlink'] = true, ['author-mask'] = true, ['authors'] = true, ['collaboration'] = true, ['date'] = true, ['df'] = true, ['display-authors'] = true, ['first'] = true, ['given'] = true, ['language'] = true, ['last'] = true, ['mode'] = true, ['name-list-style'] = true, ['no-tracking'] = true, ['p'] = true, ['page'] = true, ['pages'] = true, ['postscript'] = true, ['pp'] = true, ['quotation'] = true, ['quote'] = true, ['ref'] = true, ['surname'] = true, ['template-doc-demo'] = true, ['title'] = true, ['trans-title'] = true, ['vauthors'] = true, ['year'] = true, } local limited_numbered_arguments = { ['author#'] = true, ['author-first#'] = true, ['author#-first'] = true, ['author-given#'] = true, ['author#-given'] = true, ['author-last#'] = true, ['author#-last'] = true, ['author-surname#'] = true, ['author#-surname'] = true, ['author-link#'] = true, ['author#-link'] = true, ['authorlink#'] = true, ['author#link'] = true, ['author-mask#'] = true, ['author#-mask'] = true, ['first#'] = true, ['given#'] = true, ['last#'] = true, ['surname#'] = true, } --[[--------------------------< U N I Q U E _ A R G U M E N T S >---------------------------------------------- Some templates have unique parameters. Those templates and their unique parameters are listed here. Keys in this table are the template's CitationClass parameter value Same conventions for true/false/tracked/nil as above. ]] local unique_arguments = { ['audio-visual'] = { ['transcript'] = true, ['transcript-format'] = true, ['transcript-url'] = true, }, conference = { ['book-title'] = true, ['conference'] = true, ['conference-format'] = true, ['conference-url'] = true, ['event'] = true, }, episode = { ['airdate'] = true, ['air-date'] = true, ['credits'] = true, ['episode-link'] = true, -- alias of |title-link= ['network'] = true, ['season'] = true, ['series-link'] = true, ['series-no'] = true, ['series-number'] = true, ['station'] = true, ['transcript'] = true, ['transcript-format'] = true, ['transcripturl'] = false, ['transcript-url'] = true, }, mailinglist = { ['mailing-list'] = true, }, map = { ['cartography'] = true, ['inset'] = true, ['map'] = true, ['map-format'] = true, ['map-url'] = true, ['map-url-access'] = true, ['script-map'] = true, ['sections'] = true, ['sheet'] = true, ['sheets'] = true, ['trans-map'] = true, }, newsgroup = { ['message-id'] = true, ['newsgroup'] = true, }, report = { ['docket'] = true, }, serial = { ['airdate'] = true, ['air-date'] = true, ['credits'] = true, ['episode'] = true, -- cite serial only TODO: make available to cite episode? ['episode-link'] = true, -- alias of |title-link= ['network'] = true, ['series-link'] = true, ['station'] = true, }, speech = { ['conference'] = true, ['conference-format'] = true, ['conference-url'] = true, ['event'] = true, }, thesis = { ['degree'] = true, ['docket'] = true, }, } --[[--------------------------< T E M P L A T E _ L I S T _ G E T >-------------------------------------------- gets a list of the templates from table t ]] local function template_list_get (t) local out = {}; -- a table for output for k, _ in pairs (t) do -- spin through the table and collect the keys table.insert (out, k) -- add each key to the output table end return out; -- and done end --[[--------------------------< E X P O R T E D T A B L E S >------------------------------------------------ ]] return { basic_arguments = basic_arguments, numbered_arguments = numbered_arguments, limited_basic_arguments = limited_basic_arguments, limited_numbered_arguments = limited_numbered_arguments, preprint_arguments = preprint_arguments, preprint_template_list = template_list_get (preprint_arguments), -- make a template list from preprint_arguments{} table unique_arguments = unique_arguments, unique_param_template_list = template_list_get (unique_arguments), -- make a template list from unique_arguments{} table }; 7c70519c4a7fa5776be7289982de9107c7a95c04 Module:Citation/CS1/Utilities 828 399 799 798 2023-07-10T15:57:47Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1/Utilities]] Scribunto text/plain local z = { error_cats_t = {}; -- for categorizing citations that contain errors error_ids_t = {}; -- list of error identifiers; used to prevent duplication of certain errors; local to this module error_msgs_t = {}; -- sequence table of error messages maint_cats_t = {}; -- for categorizing citations that aren't erroneous per se, but could use a little work prop_cats_t = {}; -- for categorizing citations based on certain properties, language of source for instance prop_keys_t = {}; -- for adding classes to the citation's <cite> tag }; --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration --[[--------------------------< I S _ S E T >------------------------------------------------------------------ Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string. ]] local function is_set (var) return not (var == nil or var == ''); end --[[--------------------------< I N _ A R R A Y >-------------------------------------------------------------- Whether needle is in haystack ]] local function in_array (needle, haystack) if needle == nil then return false; end for n, v in ipairs (haystack) do if v == needle then return n; end end return false; end --[[--------------------------< H A S _ A C C E P T _ A S _ W R I T T E N >------------------------------------ When <str> is wholly wrapped in accept-as-written markup, return <str> without markup and true; return <str> and false else with allow_empty = false, <str> must have at least one character inside the markup with allow_empty = true, <str> the markup frame can be empty like (()) to distinguish an empty template parameter from the specific condition "has no applicable value" in citation-context. After further evaluation the two cases might be merged at a later stage, but should be kept separated for now. ]] local function has_accept_as_written (str, allow_empty) if not is_set (str) then return str, false; end local count; if true == allow_empty then str, count = str:gsub ('^%(%((.*)%)%)$', '%1'); -- allows (()) to be an empty set else str, count = str:gsub ('^%(%((.+)%)%)$', '%1'); end return str, 0 ~= count; end --[[--------------------------< S U B S T I T U T E >---------------------------------------------------------- Populates numbered arguments in a message string using an argument table. <args> may be a single string or a sequence table of multiple strings. ]] local function substitute (msg, args) return args and mw.message.newRawMessage (msg, args):plain() or msg; end --[[--------------------------< E R R O R _ C O M M E N T >---------------------------------------------------- Wraps error messages with CSS markup according to the state of hidden. <content> may be a single string or a sequence table of multiple strings. ]] local function error_comment (content, hidden) return substitute (hidden and cfg.presentation['hidden-error'] or cfg.presentation['visible-error'], content); end --[[--------------------------< H Y P H E N _ T O _ D A S H >-------------------------------------------------- Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are returned unmodified. These forms are modified: letter - letter (A - B) digit - digit (4-5) digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d) any other forms are returned unmodified. str may be a comma- or semicolon-separated list ]] local function hyphen_to_dash (str) if not is_set (str) then return str; end local accept; -- boolean str = str:gsub ("(%(%(.-%)%))", function(m) return m:gsub(",", ","):gsub(";", ";") end) -- replace commas and semicolons in accept-as-written markup with similar unicode characters so they'll be ignored during the split str = str:gsub ('&[nm]dash;', {['&ndash;'] = '–', ['&mdash;'] = '—'}); -- replace &mdash; and &ndash; entities with their characters; semicolon mucks up the text.split str = str:gsub ('&#45;', '-'); -- replace HTML numeric entity with hyphen character str = str:gsub ('&nbsp;', ' '); -- replace &nbsp; entity with generic keyboard space character local out = {}; local list = mw.text.split (str, '%s*[,;]%s*'); -- split str at comma or semicolon separators if there are any for _, item in ipairs (list) do -- for each item in the list item, accept = has_accept_as_written (item); -- remove accept-this-as-written markup when it wraps all of item if not accept and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit) item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter) item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or -- digit separator digit hyphen digit separator digit item:match ('^%d+%s*%-%s*%d+$') or -- digit hyphen digit item:match ('^%a+%s*%-%s*%a+$') then -- letter hyphen letter item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2'); -- replace hyphen, remove extraneous space characters else item = mw.ustring.gsub (item, '%s*[–—]%s*', '–'); -- for endash or emdash separated ranges, replace em with en, remove extraneous whitespace end end table.insert (out, item); -- add the (possibly modified) item to the output table end local temp_str = ''; -- concatenate the output table into a comma separated string temp_str, accept = has_accept_as_written (table.concat (out, ', ')); -- remove accept-this-as-written markup when it wraps all of concatenated out if accept then temp_str = has_accept_as_written (str); -- when global markup removed, return original str; do it this way to suppress boolean second return value return temp_str:gsub(",", ","):gsub(";", ";"); else return temp_str:gsub(",", ","):gsub(";", ";"); -- else, return assembled temp_str end end --[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if only link is provided (or link and display are the same), returns a wikilink in the form [[L]]; if neither are provided or link is omitted, returns an empty string. ]=] local function make_wikilink (link, display) if not is_set (link) then return '' end if is_set (display) and link ~= display then return table.concat ({'[[', link, '|', display, ']]'}); else return table.concat ({'[[', link, ']]'}); end end --[[--------------------------< S E T _ M E S S A G E >---------------------------------------------------------- Sets an error message using the ~/Configuration error_conditions{} table along with arguments supplied in the function call, inserts the resulting message in z.error_msgs_t{} sequence table, and returns the error message. <error_id> – key value for appropriate error handler in ~/Configuration error_conditions{} table <arguments> – may be a single string or a sequence table of multiple strings to be subsititued into error_conditions[error_id].message <raw> – boolean true – causes this function to return the error message not wrapped in visible-error, hidden-error span tag; returns error_conditions[error_id].hidden as a second return value does not add message to z.error_msgs_t sequence table false, nil – adds message wrapped in visible-error, hidden-error span tag to z.error_msgs_t returns the error message wrapped in visible-error, hidden-error span tag; there is no second return value <prefix> – string to be prepended to <message> -- TODO: remove support for these unused(?) arguments? <suffix> – string to be appended to <message> TODO: change z.error_cats_t and z.maint_cats_t to have the form cat_name = true? this to avoid dups without having to have an extra table ]] local added_maint_cats = {} -- list of maintenance categories that have been added to z.maint_cats_t; TODO: figure out how to delete this table local function set_message (error_id, arguments, raw, prefix, suffix) local error_state = cfg.error_conditions[error_id]; prefix = prefix or ''; suffix = suffix or ''; if error_state == nil then error (cfg.messages['undefined_error'] .. ': ' .. error_id); -- because missing error handler in Module:Citation/CS1/Configuration elseif is_set (error_state.category) then if error_state.message then -- when error_state.message defined, this is an error message table.insert (z.error_cats_t, error_state.category); else if not added_maint_cats[error_id] then added_maint_cats[error_id] = true; -- note that we've added this category table.insert (z.maint_cats_t, substitute (error_state.category, arguments)); -- make cat name then add to table end return; -- because no message, nothing more to do end end local message = substitute (error_state.message, arguments); message = table.concat ( { message, ' (', make_wikilink ( table.concat ( { cfg.messages['help page link'], '#', error_state.anchor }), cfg.messages['help page label']), ')' }); z.error_ids_t[error_id] = true; if z.error_ids_t['err_citation_missing_title'] and -- if missing-title error already noted in_array (error_id, {'err_bare_url_missing_title', 'err_trans_missing_title'}) then -- and this error is one of these return '', false; -- don't bother because one flavor of missing title is sufficient end message = table.concat ({prefix, message, suffix}); if true == raw then return message, error_state.hidden; -- return message not wrapped in visible-error, hidden-error span tag end message = error_comment (message, error_state.hidden); -- wrap message in visible-error, hidden-error span tag table.insert (z.error_msgs_t, message); -- add it to the messages sequence table return message; -- and done; return value generally not used but is used as a flag in various functions of ~/Identifiers end --[[-------------------------< I S _ A L I A S _ U S E D >----------------------------------------------------- This function is used by select_one() to determine if one of a list of alias parameters is in the argument list provided by the template. Input: args – pointer to the arguments table from calling template alias – one of the list of possible aliases in the aliases lists from Module:Citation/CS1/Configuration index – for enumerated parameters, identifies which one enumerated – true/false flag used to choose how enumerated aliases are examined value – value associated with an alias that has previously been selected; nil if not yet selected selected – the alias that has previously been selected; nil if not yet selected error_list – list of aliases that are duplicates of the alias already selected Returns: value – value associated with alias we selected or that was previously selected or nil if an alias not yet selected selected – the alias we selected or the alias that was previously selected or nil if an alias not yet selected ]] local function is_alias_used (args, alias, index, enumerated, value, selected, error_list) if enumerated then -- is this a test for an enumerated parameters? alias = alias:gsub ('#', index); -- replace '#' with the value in index else alias = alias:gsub ('#', ''); -- remove '#' if it exists end if is_set (args[alias]) then -- alias is in the template's argument list if value ~= nil and selected ~= alias then -- if we have already selected one of the aliases local skip; for _, v in ipairs (error_list) do -- spin through the error list to see if we've added this alias if v == alias then skip = true; break; -- has been added so stop looking end end if not skip then -- has not been added so table.insert (error_list, alias); -- add error alias to the error list end else value = args[alias]; -- not yet selected an alias, so select this one selected = alias; end end return value, selected; -- return newly selected alias, or previously selected alias end --[[--------------------------< A D D _ M A I N T _ C A T >------------------------------------------------------ Adds a category to z.maint_cats_t using names from the configuration file with additional text if any. To prevent duplication, the added_maint_cats table lists the categories by key that have been added to z.maint_cats_t. ]] local function add_maint_cat (key, arguments) if not added_maint_cats [key] then added_maint_cats [key] = true; -- note that we've added this category table.insert (z.maint_cats_t, substitute (cfg.maint_cats [key], arguments)); -- make name then add to table end end --[[--------------------------< A D D _ P R O P _ C A T >-------------------------------------------------------- Adds a category to z.prop_cats_t using names from the configuration file with additional text if any. foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages may be categorized but multiples of the same language are not categorized. added_prop_cats is a table declared in page scope variables above ]] local added_prop_cats = {}; -- list of property categories that have been added to z.prop_cats_t local function add_prop_cat (key, arguments, key_modifier) local key_modified = key .. ((key_modifier and key_modifier) or ''); -- modify <key> with <key_modifier> if present and not nil if not added_prop_cats [key_modified] then added_prop_cats [key_modified] = true; -- note that we've added this category table.insert (z.prop_cats_t, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table table.insert (z.prop_keys_t, 'cs1-prop-' .. key); -- convert key to class for use in the citation's <cite> tag end end --[[--------------------------< S A F E _ F O R _ I T A L I C S >---------------------------------------------- Protects a string that will be wrapped in wiki italic markup '' ... '' Note: We cannot use <i> for italics, as the expected behavior for italics specified by ''...'' in the title is that they will be inverted (i.e. unitalicized) in the resulting references. In addition, <i> and '' tend to interact poorly under Mediawiki's HTML tidy. ]] local function safe_for_italics (str) if not is_set (str) then return str end if str:sub (1, 1) == "'" then str = "<span></span>" .. str; end if str:sub (-1, -1) == "'" then str = str .. "<span></span>"; end return str:gsub ('\n', ' '); -- Remove newlines as they break italics. end --[[--------------------------< W R A P _ S T Y L E >---------------------------------------------------------- Applies styling to various parameters. Supplied string is wrapped using a message_list configuration taking one argument; protects italic styled parameters. Additional text taken from citation_config.presentation - the reason this function is similar to but separate from wrap_msg(). ]] local function wrap_style (key, str) if not is_set (str) then return ""; elseif in_array (key, {'italic-title', 'trans-italic-title'}) then str = safe_for_italics (str); end return substitute (cfg.presentation[key], {str}); end --[[--------------------------< M A K E _ S E P _ L I S T >------------------------------------------------------------ make a separated list of items using provided separators. <sep_list> - typically '<comma><space>' <sep_list_pair> - typically '<space>and<space>' <sep_list_end> - typically '<comma><space>and<space>' or '<comma><space>&<space>' defaults to cfg.presentation['sep_list'], cfg.presentation['sep_list_pair'], and cfg.presentation['sep_list_end'] if <sep_list_end> is specified, <sep_list> and <sep_list_pair> must also be supplied ]] local function make_sep_list (count, list_seq, sep_list, sep_list_pair, sep_list_end) local list = ''; if not sep_list then -- set the defaults sep_list = cfg.presentation['sep_list']; sep_list_pair = cfg.presentation['sep_list_pair']; sep_list_end = cfg.presentation['sep_list_end']; end if 2 >= count then list = table.concat (list_seq, sep_list_pair); -- insert separator between two items; returns list_seq[1] then only one item elseif 2 < count then list = table.concat (list_seq, sep_list, 1, count - 1); -- concatenate all but last item with plain list separator list = table.concat ({list, list_seq[count]}, sep_list_end); -- concatenate last item onto end of <list> with final separator end return list; end --[[--------------------------< S E L E C T _ O N E >---------------------------------------------------------- Chooses one matching parameter from a list of parameters to consider. The list of parameters to consider is just names. For parameters that may be enumerated, the position of the numerator in the parameter name is identified by the '#' so |author-last1= and |author1-last= are represented as 'author-last#' and 'author#-last'. Because enumerated parameter |<param>1= is an alias of |<param>= we must test for both possibilities. Generates an error if more than one match is present. ]] local function select_one (args, aliases_list, error_condition, index) local value = nil; -- the value assigned to the selected parameter local selected = ''; -- the name of the parameter we have chosen local error_list = {}; if index ~= nil then index = tostring(index); end for _, alias in ipairs (aliases_list) do -- for each alias in the aliases list if alias:match ('#') then -- if this alias can be enumerated if '1' == index then -- when index is 1 test for enumerated and non-enumerated aliases value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- first test for non-enumerated alias end value, selected = is_alias_used (args, alias, index, true, value, selected, error_list); -- test for enumerated alias else value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- test for non-enumerated alias end end if #error_list > 0 and 'none' ~= error_condition then -- for cases where this code is used outside of extract_names() for i, v in ipairs (error_list) do error_list[i] = wrap_style ('parameter', v); end table.insert (error_list, wrap_style ('parameter', selected)); set_message (error_condition, {make_sep_list (#error_list, error_list)}); end return value, selected; end --[=[-------------------------< R E M O V E _ W I K I _ L I N K >---------------------------------------------- Gets the display text from a wikilink like [[A|B]] or [[B]] gives B The str:gsub() returns either A|B froma [[A|B]] or B from [[B]] or B from B (no wikilink markup). In l(), l:gsub() removes the link and pipe (if they exist); the second :gsub() trims whitespace from the label if str was wrapped in wikilink markup. Presumably, this is because without wikimarkup in str, there is no match in the initial gsub, the replacement function l() doesn't get called. ]=] local function remove_wiki_link (str) return (str:gsub ("%[%[([^%[%]]*)%]%]", function(l) return l:gsub ("^[^|]*|(.*)$", "%1" ):gsub ("^%s*(.-)%s*$", "%1"); end)); end --[=[-------------------------< I S _ W I K I L I N K >-------------------------------------------------------- Determines if str is a wikilink, extracts, and returns the wikilink type, link text, and display text parts. If str is a complex wikilink ([[L|D]]): returns wl_type 2 and D and L from [[L|D]]; if str is a simple wikilink ([[D]]) returns wl_type 1 and D from [[D]] and L as empty string; if not a wikilink: returns wl_type 0, str as D, and L as empty string. trims leading and trailing whitespace and pipes from L and D ([[L|]] and [[|D]] are accepted by MediaWiki and treated like [[D]]; while [[|D|]] is not accepted by MediaWiki, here, we accept it and return D without the pipes). ]=] local function is_wikilink (str) local D, L local wl_type = 2; -- assume that str is a complex wikilink [[L|D]] if not str:match ('^%[%[[^%]]+%]%]$') then -- is str some sort of a wikilink (must have some sort of content) return 0, str, ''; -- not a wikilink; return wl_type as 0, str as D, and empty string as L end L, D = str:match ('^%[%[([^|]+)|([^%]]+)%]%]$'); -- get L and D from [[L|D]] if not is_set (D) then -- if no separate display D = str:match ('^%[%[([^%]]*)|*%]%]$'); -- get D from [[D]] or [[D|]] wl_type = 1; end D = mw.text.trim (D, '%s|'); -- trim white space and pipe characters return wl_type, D, L or ''; end --[[--------------------------< S T R I P _ A P O S T R O P H E _ M A R K U P >-------------------------------- Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata. This function strips common patterns of apostrophe markup. We presume that editors who have taken the time to markup a title have, as a result, provided valid markup. When they don't, some single apostrophes are left behind. Returns the argument without wiki markup and a number; the number is more-or-less meaningless except as a flag to indicate that markup was replaced; do not rely on it as an indicator of how many of any kind of markup was removed; returns the argument and nil when no markup removed ]] local function strip_apostrophe_markup (argument) if not is_set (argument) then return argument, nil; -- no argument, nothing to do end if nil == argument:find ( "''", 1, true ) then -- Is there at least one double apostrophe? If not, exit. return argument, nil; end local flag; while true do if argument:find ("'''''", 1, true) then -- bold italic (5) argument, flag = argument:gsub ("%'%'%'%'%'", ""); -- remove all instances of it elseif argument:find ("''''", 1, true) then -- italic start and end without content (4) argument, flag=argument:gsub ("%'%'%'%'", ""); elseif argument:find ("'''", 1, true) then -- bold (3) argument, flag=argument:gsub ("%'%'%'", ""); elseif argument:find ("''", 1, true) then -- italic (2) argument, flag = argument:gsub ("%'%'", ""); else break; end end return argument, flag; -- done end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local cfg table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr) cfg = cfg_table_ptr; end --[[--------------------------< E X P O R T S >---------------------------------------------------------------- ]] return { add_maint_cat = add_maint_cat, -- exported functions add_prop_cat = add_prop_cat, error_comment = error_comment, has_accept_as_written = has_accept_as_written, hyphen_to_dash = hyphen_to_dash, in_array = in_array, is_set = is_set, is_wikilink = is_wikilink, make_sep_list = make_sep_list, make_wikilink = make_wikilink, remove_wiki_link = remove_wiki_link, safe_for_italics = safe_for_italics, select_one = select_one, set_message = set_message, set_selected_modules = set_selected_modules, strip_apostrophe_markup = strip_apostrophe_markup, substitute = substitute, wrap_style = wrap_style, z = z, -- exported table } b006801b48981b2987f20fc09cbe0dfda525e044 Module:Citation/CS1/Date validation 828 400 801 800 2023-07-10T15:57:47Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1/Date_validation]] Scribunto text/plain --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local add_prop_cat, is_set, in_array, set_message, substitute, wrap_style; -- imported functions from selected Module:Citation/CS1/Utilities local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration --[[--------------------------< F I L E - S C O P E D E C L A R A T I O N S >-------------------------------- File-scope variables are declared here ]] local lang_object = mw.getContentLanguage(); -- used by is_valid_accessdate(), is_valid_year(), date_name_xlate(); TODO: move to ~/Configuration? local year_limit; -- used by is_valid_year() --[=[-------------------------< I S _ V A L I D _ A C C E S S D A T E >---------------------------------------- returns true if: Wikipedia start date <= accessdate < today + 2 days Wikipedia start date is 2001-01-15T00:00:00 UTC which is 979516800 seconds after 1970-01-01T00:00:00 UTC (the start of Unix time) accessdate is the date provided in |access-date= at time 00:00:00 UTC today is the current date at time 00:00:00 UTC plus 48 hours if today is 2015-01-01T00:00:00 then adding 24 hours gives 2015-01-02T00:00:00 – one second more than today adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser apparently doesn't understand non-English date month names. This function will always return false when the date contains a non-English month name because good1 is false after the call to lang.formatDate(). To get around that call this function with YYYY-MM-DD format dates. ]=] local function is_valid_accessdate (accessdate) local good1, good2; local access_ts, tomorrow_ts; -- to hold Unix time stamps representing the dates good1, access_ts = pcall (lang_object.formatDate, lang_object, 'U', accessdate ); -- convert accessdate value to Unix timestamp good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand access_ts = tonumber (access_ts) or lang_object:parseFormattedNumber (access_ts); -- convert to numbers for the comparison; tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts); else return false; -- one or both failed to convert to Unix time stamp end if 979516800 <= access_ts and access_ts < tomorrow_ts then -- Wikipedia start date <= accessdate < tomorrow's date return true; else return false; -- accessdate out of range end end --[[--------------------------< G E T _ M O N T H _ N U M B E R >---------------------------------------------- returns a number according to the month in a date: 1 for January, etc. Capitalization and spelling must be correct. If not a valid month, returns 0 ]] local function get_month_number (month) return cfg.date_names['local'].long[month] or cfg.date_names['local'].short[month] or -- look for local names first cfg.date_names['en'].long[month] or cfg.date_names['en'].short[month] or -- failing that, look for English names 0; -- not a recognized month name end --[[--------------------------< G E T _ S E A S O N _ N U M B E R >-------------------------------------------- returns a number according to the sequence of seasons in a year: 21 for Spring, etc. Capitalization and spelling must be correct. If not a valid season, returns 0. 21-24 = Spring, Summer, Autumn, Winter, independent of “Hemisphere” returns 0 when <param> is not |date= Season numbering is defined by Extended Date/Time Format (EDTF) specification (https://www.loc.gov/standards/datetime/) which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using numbers 21-41. cs1|2 only supports generic seasons. EDTF does support the distinction between north and south hemisphere seasons but cs1|2 has no way to make that distinction. These additional divisions not currently supported: 25-28 = Spring - Northern Hemisphere, Summer- Northern Hemisphere, Autumn - Northern Hemisphere, Winter - Northern Hemisphere 29-32 = Spring – Southern Hemisphere, Summer– Southern Hemisphere, Autumn – Southern Hemisphere, Winter - Southern Hemisphere 33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each) 37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each) 40-41 = Semestral 1, Semestral-2 (6 months each) ]] local function get_season_number (season, param) if 'date' ~= param then return 0; -- season dates only supported by |date= end return cfg.date_names['local'].season[season] or -- look for local names first cfg.date_names['en'].season[season] or -- failing that, look for English names 0; -- not a recognized season name end --[[--------------------------< G E T _ Q U A R T E R _ N U M B E R >------------------------------------------ returns a number according to the sequence of quarters in a year: 33 for first quarter, etc. Capitalization and spelling must be correct. If not a valid quarter, returns 0. 33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each) returns 0 when <param> is not |date= Quarter numbering is defined by Extended Date/Time Format (EDTF) specification (https://www.loc.gov/standards/datetime/) which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard defines various divisions using numbers 21-41. cs1|2 only supports generic seasons and quarters. These additional divisions not currently supported: 37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each) 40-41 = Semestral 1, Semestral-2 (6 months each) ]] local function get_quarter_number (quarter, param) if 'date' ~= param then return 0; -- quarter dates only supported by |date= end quarter = mw.ustring.gsub (quarter, ' +', ' '); -- special case replace multiple space chars with a single space char return cfg.date_names['local'].quarter[quarter] or -- look for local names first cfg.date_names['en'].quarter[quarter] or -- failing that, look for English names 0; -- not a recognized quarter name end --[[--------------------------< G E T _ P R O P E R _ N A M E _ N U M B E R >---------------------------------- returns a non-zero number if date contains a recognized proper-name. Capitalization and spelling must be correct. returns 0 when <param> is not |date= ]] local function get_proper_name_number (name, param) if 'date' ~= param then return 0; -- proper-name dates only supported by |date= end return cfg.date_names['local'].named[name] or -- look for local names dates first cfg.date_names['en'].named[name] or -- failing that, look for English names 0; -- not a recognized named date end --[[--------------------------< G E T _ E L E M E N T _ N U M B E R <------------------------------------------ returns true if month or season or quarter or proper name is valid (properly spelled, capitalized, abbreviated) ]] local function get_element_number (element, param) local num; local funcs = {get_month_number, get_season_number, get_quarter_number, get_proper_name_number}; -- list of functions to execute in order for _, func in ipairs (funcs) do -- spin through the function list num = func (element, param); -- call the function and get the returned number if 0 ~= num then -- non-zero when valid month season quarter return num; -- return that number end end return nil; -- not valid end --[[--------------------------< I S _ V A L I D _ Y E A R >---------------------------------------------------- Function gets current year from the server and compares it to year from a citation parameter. Years more than one year in the future are not acceptable. Special case for |pmc-embargo-date=: years more than two years in the future are not acceptable ]] local function is_valid_year (year, param) if not is_set (year_limit) then year_limit = tonumber(os.date("%Y"))+1; -- global variable so we only have to fetch it once end year = tonumber (year) or lang_object:parseFormattedNumber (year); -- convert to number for the comparison; if 'pmc-embargo-date' == param then -- special case for |pmc-embargo-date= return year and (year <= tonumber(os.date("%Y"))+2) or false; -- years more than two years in the future are not accepted end return year and (year <= year_limit) or false; end --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- Returns true if day is less than or equal to the number of days in month and year is no farther into the future than next year; else returns false. Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap years before 1582 and Gregorian leap years after 1582. Where the two calendars overlap (1582 to approximately 1923) dates are assumed to be Gregorian. ]] local function is_valid_date (year, month, day, param) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local month_length; if not is_valid_year (year, param) then -- no farther into the future than next year except |pmc-embargo-date= no more than two years in the future return false; end month = tonumber (month); -- required for YYYY-MM-DD dates if (2 == month) then -- if February month_length = 28; -- then 28 days unless if 1582 > tonumber(year) then -- Julian calendar if 0 == (year%4) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end else -- Gregorian calendar if (0 == (year%4) and (0 ~= (year%100) or 0 == (year%400))) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end end else month_length = days_in_month[month]; end if tonumber (day) > month_length then return false; end return true; end --[[--------------------------< I S _ V A L I D _ M O N T H _ R A N G E _ S T Y L E >-------------------------- Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August. This function looks in cfg.date_names{} to see if both month names are listed in the long subtable or both are listed in the short subtable. When both have the same style (both are listed in the same table), returns true; false else ]] local function is_valid_month_range_style (month1, month2) if (cfg.date_names.en.long[month1] and cfg.date_names.en.long[month2]) or -- are both English names listed in the long subtable? (cfg.date_names.en.short[month1] and cfg.date_names.en.short[month2]) or -- are both English names listed in the short subtable? (cfg.date_names['local'].long[month1] and cfg.date_names['local'].long[month2]) or -- are both local names listed in the long subtable? (cfg.date_names['local'].short[month1] and cfg.date_names['local'].short[month2]) then -- are both local names listed in the short subtable? return true; end return false; -- names are mixed end --[[--------------------------< I S _ V A L I D _ M O N T H _ S E A S O N _ R A N G E >------------------------ Check a pair of months or seasons to see if both are valid members of a month or season pair. Month pairs are expected to be left to right, earliest to latest in time. All season ranges are accepted as valid because there are publishers out there who have published a Summer–Spring YYYY issue, hence treat as ok ]] local function is_valid_month_season_range(range_start, range_end, param) local range_start_number = get_month_number (range_start); local range_end_number; if 0 == range_start_number then -- is this a month range? range_start_number = get_season_number (range_start, param); -- not a month; is it a season? get start season number range_end_number = get_season_number (range_end, param); -- get end season number if (0 ~= range_start_number) and (0 ~= range_end_number) and (range_start_number ~= range_end_number) then return true; -- any season pairing is accepted except when both are the same end return false; -- range_start and/or range_end is not a season end -- here when range_start is a month range_end_number = get_month_number (range_end); -- get end month number if range_start_number < range_end_number and -- range_start is a month; does range_start precede range_end? is_valid_month_range_style (range_start, range_end) then -- do months have the same style? return true; -- proper order and same style end return false; -- range_start month number is greater than or equal to range end number; or range end isn't a month end --[[--------------------------< M A K E _ C O I N S _ D A T E >------------------------------------------------ This function receives a table of date parts for one or two dates and an empty table reference declared in Module:Citation/CS1. The function is called only for |date= parameters and only if the |date=<value> is determined to be a valid date format. The question of what to do with invalid date formats is not answered here. The date parts in the input table are converted to an ISO 8601 conforming date string: single whole dates: yyyy-mm-dd month and year dates: yyyy-mm year dates: yyyy ranges: yyyy-mm-dd/yyyy-mm-dd yyyy-mm/yyyy-mm yyyy/yyyy Dates in the Julian calendar are reduced to year or year/year so that we don't have to do calendar conversion from Julian to Proleptic Gregorian. The input table has: year, year2 – always present; if before 1582, ignore months and days if present month, month2 – 0 if not provided, 1-12 for months, 21-24 for seasons; 99 Christmas day, day2 – 0 if not provided, 1-31 for days the output table receives: rftdate: an ISO 8601 formatted date rftchron: a free-form version of the date, usually without year which is in rftdate (season ranges and proper-name dates) rftssn: one of four season keywords: winter, spring, summer, fall (lowercase) rftquarter: one of four values: 1, 2, 3, 4 ]] local function make_COinS_date (input, tCOinS_date) local date; -- one date or first date in a range local date2 = ''; -- end of range date -- start temporary Julian / Gregorian calendar uncertainty detection local year = tonumber(input.year); -- this temporary code to determine the extent of sources dated to the Julian/Gregorian local month = tonumber(input.month); -- interstice 1 October 1582 – 1 January 1926 local day = tonumber (input.day); if (0 ~= day) and -- day must have a value for this to be a whole date (((1582 == year) and (10 <= month) and (12 >= month)) or -- any whole 1582 date from 1 October to 31 December or ((1926 == year) and (1 == month) and (1 == input.day)) or -- 1 January 1926 or ((1582 < year) and (1925 >= year))) then -- any date 1 January 1583 – 31 December 1925 tCOinS_date.inter_cal_cat = true; -- set category flag true end -- end temporary Julian / Gregorian calendar uncertainty detection if 1582 > tonumber(input.year) or 20 < tonumber(input.month) then -- Julian calendar or season so &rft.date gets year only date = input.year; if 0 ~= input.year2 and input.year ~= input.year2 then -- if a range, only the second year portion when not the same as range start year date = string.format ('%.4d/%.4d', tonumber(input.year), tonumber(input.year2)) -- assemble the date range end if 20 < tonumber(input.month) then -- if season or proper-name date local season = {[24] = 'winter', [21] = 'spring', [22] = 'summer', [23] = 'fall', [33] = '1', [34] = '2', [35] = '3', [36] = '4', [98] = 'Easter', [99] = 'Christmas'}; -- seasons lowercase, no autumn; proper-names use title case if 0 == input.month2 then -- single season date if 40 < tonumber(input.month) then tCOinS_date.rftchron = season[input.month]; -- proper-name dates elseif 30 < tonumber(input.month) then tCOinS_date.rftquarter = season[input.month]; -- quarters else tCOinS_date.rftssn = season[input.month]; -- seasons end else -- season range with a second season specified if input.year ~= input.year2 then -- season year – season year range or season year–year tCOinS_date.rftssn = season[input.month]; -- start of range season; keep this? if 0~= input.month2 then tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2); end else -- season–season year range tCOinS_date.rftssn = season[input.month]; -- start of range season; keep this? tCOinS_date.rftchron = season[input.month] .. '–' .. season[input.month2]; -- season–season year range end end end tCOinS_date.rftdate = date; return; -- done end if 0 ~= input.day then date = string.format ('%s-%.2d-%.2d', input.year, tonumber(input.month), tonumber(input.day)); -- whole date elseif 0 ~= input.month then date = string.format ('%s-%.2d', input.year, tonumber(input.month)); -- year and month else date = string.format ('%s', input.year); -- just year end if 0 ~= input.year2 then if 0 ~= input.day2 then date2 = string.format ('/%s-%.2d-%.2d', input.year2, tonumber(input.month2), tonumber(input.day2)); -- whole date elseif 0 ~= input.month2 then date2 = string.format ('/%s-%.2d', input.year2, tonumber(input.month2)); -- year and month else date2 = string.format ('/%s', input.year2); -- just year end end tCOinS_date.rftdate = date .. date2; -- date2 has the '/' separator return; end --[[--------------------------< P A T T E R N S >-------------------------------------------------------------- this is the list of patterns for date formats that this module recognizes. Approximately the first half of these patterns represent formats that might be reformatted into another format. Those that might be reformatted have 'indicator' letters that identify the content of the matching capture: 'd' (day), 'm' (month), 'a' (anchor year), 'y' (year); second day, month, year have a '2' suffix. These patterns are used for both date validation and for reformatting. This table should not be moved to ~/Configuration because changes to this table require changes to check_date() and to reformatter() and reformat_date() ]] local patterns = { -- year-initial numerical year-month-day ['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'}, -- month-initial: month day, year ['Mdy'] = {'^(%D-) +([1-9]%d?), +((%d%d%d%d?)%a?)$', 'm', 'd', 'a', 'y'}, -- month-initial day range: month day–day, year; days are separated by endash ['Md-dy'] = {'^(%D-) +([1-9]%d?)[%-–]([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'd2', 'a', 'y'}, -- day-initial: day month year ['dMy'] = {'^([1-9]%d?) +(%D-) +((%d%d%d%d?)%a?)$', 'd', 'm', 'a', 'y'}, -- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed; not supported at en.wiki -- ['yMd'] = {'^((%d%d%d%d?)%a?) +(%D-) +(%d%d?)$', 'a', 'y', 'm', 'd'}, -- day-range-initial: day–day month year; days are separated by endash ['d-dMy'] = {'^([1-9]%d?)[%-–]([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'd2', 'm', 'a', 'y'}, -- day initial month-day-range: day month - day month year; uses spaced endash ['dM-dMy'] = {'^([1-9]%d?) +(%D-) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'd2', 'm2', 'a', 'y'}, -- month initial month-day-range: month day – month day, year; uses spaced endash ['Md-Mdy'] = {'^(%D-) +([1-9]%d?) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$','m', 'd', 'm2', 'd2', 'a', 'y'}, -- day initial month-day-year-range: day month year - day month year; uses spaced endash ['dMy-dMy'] = {'^([1-9]%d?) +(%D-) +(%d%d%d%d) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'y', 'd2', 'm2', 'a', 'y2'}, -- month initial month-day-year-range: month day, year – month day, year; uses spaced endash ['Mdy-Mdy'] = {'^(%D-) +([1-9]%d?), +(%d%d%d%d) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'y', 'm2', 'd2', 'a', 'y2'}, -- these date formats cannot be converted, per se, but month name can be rendered short or long -- month/season year - month/season year; separated by spaced endash ['My-My'] = {'^(%D-) +(%d%d%d%d) +[%-–] +(%D-) +((%d%d%d%d)%a?)$', 'm', 'y', 'm2', 'a', 'y2'}, -- month/season range year; months separated by endash ['M-My'] = {'^(%D-)[%-–](%D-) +((%d%d%d%d)%a?)$', 'm', 'm2', 'a', 'y'}, -- month/season year or proper-name year; quarter year when First Quarter YYYY etc. ['My'] = {'^([^%d–]-) +((%d%d%d%d)%a?)$', 'm', 'a', 'y'}, -- this way because endash is a member of %D; %D- will match January–March 2019 when it shouldn't -- these date formats cannot be converted ['Sy4-y2'] = {'^(%D-) +((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash ['Sy-y'] = {'^(%D-) +(%d%d%d%d)[%-–]((%d%d%d%d)%a?)$'}, -- special case Winter/Summer year-year; year separated with unspaced endash ['y-y'] = {'^(%d%d%d%d?)[%-–]((%d%d%d%d?)%a?)$'}, -- year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999 ['y4-y2'] = {'^((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- year range: YYYY–YY; separated by unspaced endash ['y'] = {'^((%d%d%d%d?)%a?)$'}, -- year; here accept either YYY or YYYY } --[[--------------------------< I S _ V A L I D _ E M B A R G O _ D A T E >------------------------------------ returns true and date value if that value has proper dmy, mdy, ymd format. returns false and 9999 (embargoed forever) when date value is not proper format; assumes that when |pmc-embargo-date= is set, the editor intended to embargo a PMC but |pmc-embargo-date= does not hold a single date. ]] local function is_valid_embargo_date (v) if v:match (patterns['ymd'][1]) or -- ymd v:match (patterns['Mdy'][1]) or -- dmy v:match (patterns['dMy'][1]) then -- mdy return true, v; end return false, '9999'; -- if here not good date so return false and set embargo date to long time in future end --[[--------------------------< C H E C K _ D A T E >---------------------------------------------------------- Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only allowed range separator is endash. Additionally, check the date to see that it is a real date: no 31 in 30-day months; no 29 February when not a leap year. Months, both long-form and three character abbreviations, and seasons must be spelled correctly. Future years beyond next year are not allowed. If the date fails the format tests, this function returns false and does not return values for anchor_year and COinS_date. When this happens, the date parameter is (DEBUG: not?) used in the COinS metadata and the CITEREF identifier gets its year from the year parameter if present otherwise CITEREF does not get a date value. Inputs: date_string - date string from date-holding parameters (date, year, publication-date, access-date, pmc-embargo-date, archive-date, lay-date) Returns: false if date string is not a real date; else true, anchor_year, COinS_date anchor_year can be used in CITEREF anchors COinS_date is ISO 8601 format date; see make_COInS_date() ]] local function check_date (date_string, param, tCOinS_date) local year; -- assume that year2, months, and days are not used; local year2 = 0; -- second year in a year range local month = 0; local month2 = 0; -- second month in a month range local day = 0; local day2 = 0; -- second day in a day range local anchor_year; local coins_date; if date_string:match (patterns['ymd'][1]) then -- year-initial numerical year month day format year, month, day = date_string:match (patterns['ymd'][1]); if 12 < tonumber(month) or 1 > tonumber(month) or 1582 > tonumber(year) or 0 == tonumber(day) then return false; end -- month or day number not valid or not Gregorian calendar anchor_year = year; elseif mw.ustring.match(date_string, patterns['Mdy'][1]) then -- month-initial: month day, year month, day, anchor_year, year = mw.ustring.match(date_string, patterns['Mdy'][1]); month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months elseif mw.ustring.match(date_string, patterns['Md-dy'][1]) then -- month-initial day range: month day–day, year; days are separated by endash month, day, day2, anchor_year, year = mw.ustring.match(date_string, patterns['Md-dy'][1]); if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same; month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months month2=month; -- for metadata year2 = year; elseif mw.ustring.match(date_string, patterns['dMy'][1]) then -- day-initial: day month year day, month, anchor_year, year = mw.ustring.match(date_string, patterns['dMy'][1]); month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months --[[ NOT supported at en.wiki elseif mw.ustring.match(date_string, patterns['yMd'][1]) then -- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed anchor_year, year, month, day = mw.ustring.match(date_string, patterns['yMd'][1]); month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months -- end NOT supported at en.wiki ]] elseif mw.ustring.match(date_string, patterns['d-dMy'][1]) then -- day-range-initial: day–day month year; days are separated by endash day, day2, month, anchor_year, year = mw.ustring.match(date_string, patterns['d-dMy'][1]); if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same; month = get_month_number (month); if 0 == month then return false; end -- return false if month text isn't one of the twelve months month2 = month; -- for metadata year2 = year; elseif mw.ustring.match(date_string, patterns['dM-dMy'][1]) then -- day initial month-day-range: day month - day month year; uses spaced endash day, month, day2, month2, anchor_year, year = mw.ustring.match(date_string, patterns['dM-dMy'][1]); if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end -- date range order is left to right: earlier to later; month = get_month_number (month); -- for metadata month2 = get_month_number (month2); year2 = year; elseif mw.ustring.match(date_string, patterns['Md-Mdy'][1]) then -- month initial month-day-range: month day – month day, year; uses spaced endash month, day, month2, day2, anchor_year, year = mw.ustring.match(date_string, patterns['Md-Mdy'][1]); if (not is_valid_month_season_range(month, month2, param)) or not is_valid_year(year) then return false; end month = get_month_number (month); -- for metadata month2 = get_month_number (month2); year2 = year; elseif mw.ustring.match(date_string, patterns['dMy-dMy'][1]) then -- day initial month-day-year-range: day month year - day month year; uses spaced endash day, month, year, day2, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['dMy-dMy'][1]); if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style month = get_month_number (month); -- for metadata month2 = get_month_number (month2); if 0 == month or 0 == month2 then return false; end -- both must be valid elseif mw.ustring.match(date_string, patterns['Mdy-Mdy'][1]) then -- month initial month-day-year-range: month day, year – month day, year; uses spaced endash month, day, year, month2, day2, anchor_year, year2 = mw.ustring.match(date_string, patterns['Mdy-Mdy'][1]); if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style month = get_month_number (month); -- for metadata month2 = get_month_number(month2); if 0 == month or 0 == month2 then return false; end -- both must be valid elseif mw.ustring.match(date_string, patterns['Sy4-y2'][1]) then -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash local century; month, year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy4-y2'][1]); if 'Winter' ~= month and 'Summer' ~= month then return false end; -- 'month' can only be Winter or Summer anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years year2 = century..year2; -- add the century to year2 for comparisons if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) then return false; end -- no year farther in the future than next year month = get_season_number(month, param); elseif mw.ustring.match(date_string, patterns['Sy-y'][1]) then -- special case Winter/Summer year-year; year separated with unspaced endash month, year, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy-y'][1]); month = get_season_number (month, param); -- <month> can only be winter or summer; also for metadata if (month ~= cfg.date_names['en'].season['Winter']) and (month ~= cfg.date_names['en'].season['Summer']) then return false; -- not Summer or Winter; abandon end anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later if not is_valid_year(year2) then return false; end -- no year farther in the future than next year elseif mw.ustring.match(date_string, patterns['My-My'][1]) then -- month/season year - month/season year; separated by spaced endash month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['My-My'][1]); anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same if not is_valid_year(year2) then return false; end -- no year farther in the future than next year if 0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2) then -- both must be month year, same month style month = get_month_number(month); month2 = get_month_number(month2); elseif 0 ~= get_season_number(month, param) and 0 ~= get_season_number(month2, param) then -- both must be season year, not mixed month = get_season_number(month, param); month2 = get_season_number(month2, param); else return false; end elseif mw.ustring.match(date_string, patterns['M-My'][1]) then -- month/season range year; months separated by endash month, month2, anchor_year, year = mw.ustring.match(date_string, patterns['M-My'][1]); if (not is_valid_month_season_range(month, month2, param)) or (not is_valid_year(year)) then return false; end if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season month = get_month_number(month); month2 = get_month_number(month2); if 0 == month or 0 == month2 then return false; end else month = get_season_number(month, param); month2 = get_season_number(month2, param); end year2 = year; elseif mw.ustring.match(date_string, patterns['My'][1]) then -- month/season/quarter/proper-name year month, anchor_year, year = mw.ustring.match(date_string, patterns['My'][1]); if not is_valid_year(year) then return false; end month = get_element_number(month, param); -- get month season quarter proper-name number or nil if not month then return false; end -- not valid whatever it is elseif mw.ustring.match(date_string, patterns['y-y'][1]) then -- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999 year, anchor_year, year2 = mw.ustring.match(date_string, patterns['y-y'][1]); anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same if not is_valid_year(year2) then return false; end -- no year farther in the future than next year elseif mw.ustring.match(date_string, patterns['y4-y2'][1]) then -- Year range: YYYY–YY; separated by unspaced endash local century; year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['y4-y2'][1]); anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years if in_array (param, {'date', 'publication-date', 'year'}) then add_prop_cat ('year-range-abbreviated'); end if 13 > tonumber(year2) then return false; end -- don't allow 2003-05 which might be May 2003 year2 = century .. year2; -- add the century to year2 for comparisons if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same if not is_valid_year(year2) then return false; end -- no year farther in the future than next year elseif mw.ustring.match(date_string, patterns['y'][1]) then -- year; here accept either YYY or YYYY anchor_year, year = mw.ustring.match(date_string, patterns['y'][1]); if false == is_valid_year(year) then return false; end else return false; -- date format not one of the MOS:DATE approved formats end if param ~= 'date' then -- CITEREF disambiguation only allowed in |date=; |year= & |publication-date= promote to date if anchor_year:match ('%l$') then return false; end end if 'access-date' == param then -- test accessdate here because we have numerical date parts if 0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required 0 == year2 and 0 == month2 and 0 == day2 then -- none of these; accessdate must not be a range if not is_valid_accessdate(year .. '-' .. month .. '-' .. day) then return false; -- return false when accessdate out of bounds end else return false; -- return false when accessdate is a range of two dates end end local result=true; -- check whole dates for validity; assume true because not all dates will go through this test if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then -- YMD (simple whole date) result = is_valid_date (year, month, day, param); -- <param> for |pmc-embargo-date= elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then -- YMD-d (day range) result = is_valid_date (year, month, day); result = result and is_valid_date (year, month, day2); elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-md (day month range) result = is_valid_date (year, month, day); result = result and is_valid_date (year, month2, day2); elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 ~= year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-ymd (day month year range) result = is_valid_date(year, month, day); result = result and is_valid_date(year2, month2, day2); end if false == result then return false; end if nil ~= tCOinS_date then -- this table only passed into this function when testing |date= parameter values make_COinS_date ({year = year, month = month, day = day, year2 = year2, month2 = month2, day2 = day2}, tCOinS_date); -- make an ISO 8601 date string for COinS end return true, anchor_year; -- format is good and date string represents a real date end --[[--------------------------< D A T E S >-------------------------------------------------------------------- Cycle the date-holding parameters in passed table date_parameters_list through check_date() to check compliance with MOS:DATE. For all valid dates, check_date() returns true. The |date= parameter test is unique, it is the only date holding parameter from which values for anchor_year (used in CITEREF identifiers) and COinS_date (used in the COinS metadata) are derived. The |date= parameter is the only date-holding parameter that is allowed to contain the no-date keywords "n.d." or "nd" (without quotes). Unlike most error messages created in this module, only one error message is created by this function. Because all of the date holding parameters are processed serially, parameters with errors are added to the <error_list> sequence table as the dates are tested. ]] local function dates(date_parameters_list, tCOinS_date, error_list) local anchor_year; -- will return as nil if the date being tested is not |date= local COinS_date; -- will return as nil if the date being tested is not |date= local embargo_date; -- if embargo date is a good dmy, mdy, ymd date then holds original value else reset to 9999 local good_date = false; for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list if is_set(v.val) then -- if the parameter has a value v.val = mw.ustring.gsub(v.val, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9 if v.val:match("^c%. [1-9]%d%d%d?%a?$") then -- special case for c. year or with or without CITEREF disambiguator - only |date= and |year= local year = v.val:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested if 'date' == k then anchor_year, COinS_date = v.val:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter good_date = is_valid_year(year); elseif 'year' == k then good_date = is_valid_year(year); end elseif 'date' == k then -- if the parameter is |date= if v.val:match("^n%.d%.%a?$") then -- ToDo: I18N -- if |date=n.d. with or without a CITEREF disambiguator good_date, anchor_year, COinS_date = true, v.val:match("((n%.d%.)%a?)"); -- ToDo: I18N -- "n.d."; no error when date parameter is set to no date elseif v.val:match("^nd%a?$") then -- ToDo: I18N -- if |date=nd with or without a CITEREF disambiguator good_date, anchor_year, COinS_date = true, v.val:match("((nd)%a?)"); -- ToDo: I18N -- "nd"; no error when date parameter is set to no date else good_date, anchor_year, COinS_date = check_date (v.val, k, tCOinS_date); -- go test the date end elseif 'year' == k then -- if the parameter is |year= it should hold only a year value if v.val:match("^[1-9]%d%d%d?%a?$") then -- if |year = 3 or 4 digits only with or without a CITEREF disambiguator good_date, anchor_year, COinS_date = true, v.val:match("((%d+)%a?)"); end elseif 'pmc-embargo-date' == k then -- if the parameter is |pmc-embargo-date= good_date = check_date (v.val, k); -- go test the date if true == good_date then -- if the date is a valid date good_date, embargo_date = is_valid_embargo_date (v.val); -- is |pmc-embargo-date= date a single dmy, mdy, or ymd formatted date? yes: returns embargo date; no: returns 9999 end else -- any other date-holding parameter good_date = check_date (v.val, k); -- go test the date end if false == good_date then -- assemble one error message so we don't add the tracking category multiple times table.insert (error_list, wrap_style ('parameter', v.name)); -- make parameter name suitable for error message list end end end return anchor_year, embargo_date; -- and done end --[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------ Compare the value provided in |year= with the year value(s) provided in |date=. This function sets a local numeric value: 0 - year value does not match the year value in date 1 - (default) year value matches the year value in date or one of the year values when date contains two years 2 - year value matches the year value in date when date is in the form YYYY-MM-DD and year is disambiguated (|year=YYYYx) the numeric value in <result> determines the 'output' if any from this function: 0 – adds error message to error_list sequence table 1 – adds maint cat 2 – does nothing ]] local function year_date_check (year_string, year_origin, date_string, date_origin, error_list) local year; local date1; local date2; local result = 1; -- result of the test; assume that the test passes year = year_string:match ('(%d%d%d%d?)'); if date_string:match ('%d%d%d%d%-%d%d%-%d%d') and year_string:match ('%d%d%d%d%a') then --special case where both date and year are required YYYY-MM-DD and YYYYx date1 = date_string:match ('(%d%d%d%d)'); year = year_string:match ('(%d%d%d%d)'); if year ~= date1 then result = 0; -- years don't match else result = 2; -- years match; but because disambiguated, don't add to maint cat end elseif date_string:match ("%d%d%d%d?.-%d%d%d%d?") then -- any of the standard range formats of date with two three- or four-digit years date1, date2 = date_string:match ("(%d%d%d%d?).-(%d%d%d%d?)"); if year ~= date1 and year ~= date2 then result = 0; end elseif mw.ustring.match(date_string, "%d%d%d%d[%-–]%d%d") then -- YYYY-YY date ranges local century; date1, century, date2 = mw.ustring.match(date_string, "((%d%d)%d%d)[%-–]+(%d%d)"); date2 = century..date2; -- convert YY to YYYY if year ~= date1 and year ~= date2 then result = 0; end elseif date_string:match ("%d%d%d%d?") then -- any of the standard formats of date with one year date1 = date_string:match ("(%d%d%d%d?)"); if year ~= date1 then result = 0; end else -- should never get here; this function called only when no other date errors result = 0; -- no recognizable year in date end if 0 == result then -- year / date mismatch table.insert (error_list, substitute (cfg.messages['mismatch'], {year_origin, date_origin})); -- add error message to error_list sequence table elseif 1 == result then -- redundant year / date set_message ('maint_date_year'); -- add a maint cat end end --[[--------------------------< R E F O R M A T T E R >-------------------------------------------------------- reformat 'date' into new format specified by format_param if pattern_idx (the current format of 'date') can be reformatted. Does the grunt work for reformat_dates(). The table re_formats maps pattern_idx (current format) and format_param (desired format) to a table that holds: format string used by string.format() identifier letters ('d', 'm', 'y', 'd2', 'm2', 'y2') that serve as indexes into a table t{} that holds captures from mw.ustring.match() for the various date parts specified by patterns[pattern_idx][1] Items in patterns{} have the general form: ['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'}, where: ['ymd'] is pattern_idx patterns['ymd'][1] is the match pattern with captures for mw.ustring.match() patterns['ymd'][2] is an indicator letter identifying the content of the first capture patterns['ymd'][3] ... the second capture etc. when a pattern matches a date, the captures are loaded into table t{} in capture order using the idemtifier characters as indexes into t{} For the above, a ymd date is in t{} as: t.y = first capture (year), t.m = second capture (month), t.d = third capture (day) To reformat, this function is called with the pattern_idx that matches the current format of the date and with format_param set to the desired format. This function loads table t{} as described and then calls string.format() with the format string specified by re_format[pattern_idx][format_param][1] using values taken from t{} according to the capture identifier letters specified by patterns[pattern_idx][format_param][n] where n is 2.. ]] local re_formats = { ['ymd'] = { -- date format is ymd; reformat to: ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki }, ['Mdy'] = { -- date format is Mdy; reformat to: ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- for long/short reformatting ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki }, ['dMy'] = { -- date format is dMy; reformat to: ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- for long/short reformatting ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd -- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki }, ['Md-dy'] = { -- date format is Md-dy; reformat to: ['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- for long/short reformatting ['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- |df=dmy -> d-dMy }, ['d-dMy'] = { -- date format is d-d>y; reformat to: ['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- for long/short reformatting ['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- |df=mdy -> Md-dy }, ['dM-dMy'] = { -- date format is dM-dMy; reformat to: ['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- for long/short reformatting ['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- |df=mdy -> Md-Mdy }, ['Md-Mdy'] = { -- date format is Md-Mdy; reformat to: ['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- for long/short reformatting ['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- |df=dmy -> dM-dMy }, ['dMy-dMy'] = { -- date format is dMy-dMy; reformat to: ['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- for long/short reformatting ['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- |df=mdy -> Mdy-Mdy }, ['Mdy-Mdy'] = { -- date format is Mdy-Mdy; reformat to: ['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- for long/short reformatting ['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- |df=dmy -> dMy-dMy }, ['My-My'] = { -- these for long/short reformatting ['any'] = {'%s %s – %s %s', 'm', 'y', 'm2', 'y2'}, -- dmy/mdy agnostic }, ['M-My'] = { -- these for long/short reformatting ['any'] = {'%s–%s %s', 'm', 'm2', 'y'}, -- dmy/mdy agnostic }, ['My'] = { -- these for long/short reformatting ['any'] = {'%s %s', 'm', 'y'}, -- dmy/mdy agnostic }, -- ['yMd'] = { -- not supported at en.wiki -- ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy -- ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy -- ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd -- }, } local function reformatter (date, pattern_idx, format_param, mon_len) if not in_array (pattern_idx, {'ymd', 'Mdy', 'Md-dy', 'dMy', 'yMd', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then return; -- not in this set of date format patterns then not a reformattable date end if 'ymd' == format_param and in_array (pattern_idx, {'ymd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then return; -- ymd date ranges not supported at en.wiki; no point in reformatting ymd to ymd end if in_array (pattern_idx, {'My', 'M-My', 'My-My'}) then -- these are not dmy/mdy so can't be 'reformatted' into either format_param = 'any'; -- so format-agnostic end -- yMd is not supported at en.wiki; when yMd is supported at your wiki, uncomment the next line -- if 'yMd' == format_param and in_array (pattern_idx, {'yMd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy'}) then -- these formats not convertable; yMd not supported at en.wiki if 'yMd' == format_param then -- yMd not supported at en.wiki; when yMd is supported at your wiki, remove or comment-out this line return; -- not a reformattable date end local c1, c2, c3, c4, c5, c6, c7; -- these hold the captures specified in patterns[pattern_idx][1] c1, c2, c3, c4, c5, c6, c7 = mw.ustring.match (date, patterns[pattern_idx][1]); -- get the captures local t = { -- table that holds k/v pairs of date parts from the captures and patterns[pattern_idx][2..] [patterns[pattern_idx][2]] = c1; -- at minimum there is always one capture with a matching indicator letter [patterns[pattern_idx][3] or 'x'] = c2; -- patterns can have a variable number of captures; each capture requires an indicator letter; [patterns[pattern_idx][4] or 'x'] = c3; -- where there is no capture, there is no indicator letter so n in patterns[pattern_idx][n] will be nil; [patterns[pattern_idx][5] or 'x'] = c4; -- the 'x' here spoofs an indicator letter to prevent 'table index is nil' error [patterns[pattern_idx][6] or 'x'] = c5; [patterns[pattern_idx][7] or 'x'] = c6; [patterns[pattern_idx][8] or 'x'] = c7; }; if t.a then -- if this date has an anchor year capture (all convertable date formats except ymd) if t.y2 then -- for year range date formats t.y2 = t.a; -- use the anchor year capture when reassembling the date else -- here for single date formats (except ymd) t.y = t.a; -- use the anchor year capture when reassembling the date end end if tonumber(t.m) then -- if raw month is a number (converting from ymd) if 's' == mon_len then -- if we are to use abbreviated month names t.m = cfg.date_names['inv_local_short'][tonumber(t.m)]; -- convert it to a month name else t.m = cfg.date_names['inv_local_long'][tonumber(t.m)]; -- convert it to a month name end t.d = t.d:gsub ('0(%d)', '%1'); -- strip leading '0' from day if present elseif 'ymd' == format_param then -- when converting to ymd t.y = t.y:gsub ('%a', ''); -- strip CITREF disambiguator if present; anchor year already known so process can proceed; TODO: maint message? if 1582 > tonumber (t.y) then -- ymd format dates not allowed before 1582 return; end t.m = string.format ('%02d', get_month_number (t.m)); -- make sure that month and day are two digits t.d = string.format ('%02d', t.d); elseif mon_len then -- if mon_len is set to either 'short' or 'long' for _, mon in ipairs ({'m', 'm2'}) do -- because there can be two month names, check both if t[mon] then t[mon] = get_month_number (t[mon]); -- get the month number for this month (is length agnostic) if 0 == t[mon] then return; end -- seasons and named dates can't be converted t[mon] = (('s' == mon_len) and cfg.date_names['inv_local_short'][t[mon]]) or cfg.date_names['inv_local_long'][t[mon]]; -- fetch month name according to length end end end local new_date = string.format (re_formats[pattern_idx][format_param][1], -- format string t[re_formats[pattern_idx][format_param][2]], -- named captures from t{} t[re_formats[pattern_idx][format_param][3]], t[re_formats[pattern_idx][format_param][4]], t[re_formats[pattern_idx][format_param][5]], t[re_formats[pattern_idx][format_param][6]], t[re_formats[pattern_idx][format_param][7]], t[re_formats[pattern_idx][format_param][8]] ); return new_date; end --[[-------------------------< R E F O R M A T _ D A T E S >-------------------------------------------------- Reformats existing dates into the format specified by format. format is one of several manual keywords: dmy, dmy-all, mdy, mdy-all, ymd, ymd-all. The -all version includes access- and archive-dates; otherwise these dates are not reformatted. This function allows automatic date formatting. In ~/Configuration, the article source is searched for one of the {{use xxx dates}} templates. If found, xxx becomes the global date format as xxx-all. If |cs1-dates= in {{use xxx dates}} has legitimate value then that value determines how cs1|2 dates will be rendered. Legitimate values for |cs1-dates= are: l - all dates are rendered with long month names ls - publication dates use long month names; access-/archive-dates use abbreviated month names ly - publication dates use long month names; access-/archive-dates rendered in ymd format s - all dates are rendered with abbreviated (short) month names sy - publication dates use abbreviated month names; access-/archive-dates rendered in ymd format y - all dates are rendered in ymd format the format argument for automatic date formatting will be the format specified by {{use xxx dates}} with the value supplied by |cs1-dates so one of: xxx-l, xxx-ls, xxx-ly, xxx-s, xxx-sy, xxx-y, or simply xxx (|cs1-dates= empty, omitted, or invalid) where xxx shall be either of dmy or mdy. dates are extracted from date_parameters_list, reformatted (if appropriate), and then written back into the list in the new format. Dates in date_parameters_list are presumed here to be valid (no errors). This function returns true when a date has been reformatted, false else. Actual reformatting is done by reformatter(). ]] local function reformat_dates (date_parameters_list, format) local all = false; -- set to false to skip access- and archive-dates local len_p = 'l'; -- default publication date length shall be long local len_a = 'l'; -- default access-/archive-date length shall be long local result = false; local new_date; if format:match('%a+%-all') then -- manual df keyword; auto df keyword when length not specified in {{use xxx dates}}; format = format:match('(%a+)%-all'); -- extract the format all = true; -- all dates are long format dates because this keyword doesn't specify length elseif format:match('%a+%-[lsy][sy]?') then -- auto df keywords; internal only all = true; -- auto df applies to all dates; use length specified by capture len_p for all dates format, len_p, len_a = format:match('(%a+)%-([lsy])([sy]?)'); -- extract the format and length keywords if 'y' == len_p then -- because allowed by MOS:DATEUNIFY (sort of) range dates and My dates not reformatted format = 'ymd'; -- override {{use xxx dates}} elseif (not is_set(len_a)) or (len_p == len_a) then -- no access-/archive-date length specified or same length as publication dates then len_a = len_p; -- in case len_a not set end end -- else only publication dates and they are long for param_name, param_val in pairs (date_parameters_list) do -- for each date-holding parameter in the list if is_set (param_val.val) then -- if the parameter has a value if not (not all and in_array (param_name, {'access-date', 'archive-date'})) then -- skip access- or archive-date unless format is xxx-all; yeah, ugly; TODO: find a better way for pattern_idx, pattern in pairs (patterns) do if mw.ustring.match (param_val.val, pattern[1]) then if all and in_array (param_name, {'access-date', 'archive-date'}) then -- if this date is an access- or archive-date new_date = reformatter (param_val.val, pattern_idx, (('y' == len_a) and 'ymd') or format, len_a); -- choose ymd or dmy/mdy according to len_a setting else -- all other dates new_date = reformatter (param_val.val, pattern_idx, format, len_p); end if new_date then -- set when date was reformatted date_parameters_list[param_name].val = new_date; -- update date in date list result = true; -- and announce that changes have been made end end -- if end -- for end -- if end -- if end -- for return result; -- declare boolean result and done end --[[--------------------------< D A T E _ H Y P H E N _ T O _ D A S H >---------------------------------------- Loops through the list of date-holding parameters and converts any hyphen to an ndash. Not called if the cs1|2 template has any date errors. Modifies the date_parameters_list and returns true if hyphens are replaced, else returns false. ]] local function date_hyphen_to_dash (date_parameters_list) local result = false; local n; for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list if is_set (param_val.val) and not mw.ustring.match (param_val.val, patterns.ymd[1]) then -- for those that are not ymd dates (ustring because here digits may not be Western) param_val.val, n = param_val.val:gsub ('%-', '–'); -- replace any hyphen with ndash if 0 ~= n then date_parameters_list[param_name].val = param_val.val; -- update the list result = true; end end end return result; -- so we know if any hyphens were replaced end --[[-------------------------< D A T E _ N A M E _ X L A T E >------------------------------------------------ Attempts to translate English date names to local-language date names using names supplied by MediaWiki's date parser function. This is simple name-for-name replacement and may not work for all languages. if xlat_dig is true, this function will also translate Western (English) digits to the local language's digits. This will also translate ymd dates. ]] local function date_name_xlate (date_parameters_list, xlt_dig) local xlate; local mode; -- long or short month names local modified = false; local date; local sources_t = { {cfg.date_names.en.long, cfg.date_names.inv_local_long}, -- for translating long English month names to long local month names {cfg.date_names.en.short, cfg.date_names.inv_local_short}, -- short month names {cfg.date_names.en.quarter, cfg.date_names.inv_local_quarter}, -- quarter date names {cfg.date_names.en.season, cfg.date_names.inv_local_season}, -- season date nam {cfg.date_names.en.named, cfg.date_names.inv_local_named}, -- named dates } local function is_xlateable (month) -- local function to get local date name that replaces existing English-language date name for _, date_names_t in ipairs (sources_t) do -- for each sequence table in date_names_t if date_names_t[1][month] then -- if date name is English month (long or short), quarter, season or named and if date_names_t[2][date_names_t[1][month]] then -- if there is a matching local date name return date_names_t[2][date_names_t[1][month]]; -- return the local date name end end end end for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list if is_set(param_val.val) then -- if the parameter has a value date = param_val.val; for month in mw.ustring.gmatch (date, '[%a ]+') do -- iterate through all date names in the date (single date or date range) month = mw.text.trim (month); -- this because quarterly dates contain whitespace xlate = is_xlateable (month); -- get translate <month>; returns translation or nil if xlate then date = mw.ustring.gsub (date, month, xlate); -- replace the English with the translation date_parameters_list[param_name].val = date; -- save the translated date modified = true; end end if xlt_dig then -- shall we also translate digits? date = date:gsub ('%d', cfg.date_names.xlate_digits); -- translate digits from Western to 'local digits' date_parameters_list[param_name].val = date; -- save the translated date modified = true; end end end return modified; end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local imported functions table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr, utilities_page_ptr) add_prop_cat = utilities_page_ptr.add_prop_cat ; -- import functions from selected Module:Citation/CS1/Utilities module is_set = utilities_page_ptr.is_set; in_array = utilities_page_ptr.in_array; set_message = utilities_page_ptr.set_message; substitute = utilities_page_ptr.substitute; wrap_style = utilities_page_ptr.wrap_style; cfg = cfg_table_ptr; -- import tables from selected Module:Citation/CS1/Configuration end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return { -- return exported functions dates = dates, year_date_check = year_date_check, reformat_dates = reformat_dates, date_hyphen_to_dash = date_hyphen_to_dash, date_name_xlate = date_name_xlate, set_selected_modules = set_selected_modules } 46ec997eed12f96ed5a030aee3a4264622c84955 Module:Citation/CS1/Identifiers 828 401 803 802 2023-07-10T15:57:48Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1/Identifiers]] Scribunto text/plain --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local has_accept_as_written, is_set, in_array, set_message, select_one, -- functions in Module:Citation/CS1/Utilities substitute, make_wikilink; local z; -- table of tables defined in Module:Citation/CS1/Utilities local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration --[[--------------------------< P A G E S C O P E V A R I A B L E S >-------------------------------------- declare variables here that have page-wide scope that are not brought in from other modules; that are created here and used here ]] local auto_link_urls = {}; -- holds identifier URLs for those identifiers that can auto-link |title= --============================<< H E L P E R F U N C T I O N S >>============================================ --[[--------------------------< W I K I D A T A _ A R T I C L E _ N A M E _ G E T >---------------------------- as an aid to internationalizing identifier-label wikilinks, gets identifier article names from Wikidata. returns :<lang code>:<article title> when <q> has an <article title> for <lang code>; nil else for identifiers that do not have q, returns nil for wikis that do not have mw.wikibase installed, returns nil ]] local function wikidata_article_name_get (q) if not is_set (q) or (q and not mw.wikibase) then -- when no q number or when a q number but mw.wikibase not installed on this wiki return nil; -- abandon end local wd_article; local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org wd_article = mw.wikibase.getSitelink (q, this_wiki_code .. 'wiki'); -- fetch article title from WD; nil when no title available at this wiki if wd_article then wd_article = table.concat ({':', this_wiki_code, ':', wd_article}); -- interwiki-style link without brackets if taken from WD; leading colon required end return wd_article; -- article title from WD; nil else end --[[--------------------------< L I N K _ L A B E L _ M A K E >------------------------------------------------ common function to create identifier link label from handler table or from Wikidata returns the first available of 1. redirect from local wiki's handler table (if enabled) 2. Wikidata (if there is a Wikidata entry for this identifier in the local wiki's language) 3. label specified in the local wiki's handler table ]] local function link_label_make (handler) local wd_article; if not (cfg.use_identifier_redirects and is_set (handler.redirect)) then -- redirect has priority so if enabled and available don't fetch from Wikidata because expensive wd_article = wikidata_article_name_get (handler.q); -- if Wikidata has an article title for this wiki, get it; end return (cfg.use_identifier_redirects and is_set (handler.redirect) and handler.redirect) or wd_article or handler.link; end --[[--------------------------< E X T E R N A L _ L I N K _ I D >---------------------------------------------- Formats a wiki-style external link ]] local function external_link_id (options) local url_string = options.id; local ext_link; local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org local wd_article; -- article title from Wikidata if options.encode == true or options.encode == nil then url_string = mw.uri.encode (url_string, 'PATH'); end if options.auto_link and is_set (options.access) then auto_link_urls[options.auto_link] = table.concat ({options.prefix, url_string, options.suffix}); end ext_link = mw.ustring.format ('[%s%s%s %s]', options.prefix, url_string, options.suffix or "", mw.text.nowiki (options.id)); if is_set (options.access) then ext_link = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[options.access].class, cfg.presentation[options.access].title, ext_link}); -- add the free-to-read / paywall lock end return table.concat ({ make_wikilink (link_label_make (options), options.label), -- redirect, Wikidata link, or locally specified link (in that order) options.separator or '&nbsp;', ext_link }); end --[[--------------------------< I N T E R N A L _ L I N K _ I D >---------------------------------------------- Formats a wiki-style internal link TODO: Does not currently need to support options.access, options.encode, auto-linking and COinS (as in external_link_id), but may be needed in the future for :m:Interwiki_map custom-prefixes like :arxiv:, :bibcode:, :DOI:, :hdl:, :ISSN:, :JSTOR:, :Openlibrary:, :PMID:, :RFC:. ]] local function internal_link_id (options) local id = mw.ustring.gsub (options.id, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9 return table.concat ( { make_wikilink (link_label_make (options), options.label), -- wiki-link the identifier label options.separator or '&nbsp;', -- add the separator make_wikilink ( table.concat ( { options.prefix, id, -- translated to Western digits options.suffix or '' }), substitute (cfg.presentation['bdi'], {'', mw.text.nowiki (options.id)}) -- bdi tags to prevent Latin script identifiers from being reversed at RTL language wikis ); -- nowiki because MediaWiki still has magic links for ISBN and the like; TODO: is it really required? }); end --[[--------------------------< I S _ E M B A R G O E D >------------------------------------------------------ Determines if a PMC identifier's online version is embargoed. Compares the date in |pmc-embargo-date= against today's date. If embargo date is in the future, returns the content of |pmc-embargo-date=; otherwise, returns an empty string because the embargo has expired or because |pmc-embargo-date= was not set in this cite. ]] local function is_embargoed (embargo) if is_set (embargo) then local lang = mw.getContentLanguage(); local good1, embargo_date, todays_date; good1, embargo_date = pcall (lang.formatDate, lang, 'U', embargo); todays_date = lang:formatDate ('U'); if good1 then -- if embargo date is a good date if tonumber (embargo_date) >= tonumber (todays_date) then -- is embargo date is in the future? return embargo; -- still embargoed else set_message ('maint_pmc_embargo'); -- embargo has expired; add main cat return ''; -- unset because embargo has expired end end end return ''; -- |pmc-embargo-date= not set return empty string end --[=[-------------------------< I S _ V A L I D _ B I O R X I V _ D A T E >------------------------------------ returns true if: 2019-12-11T00:00Z <= biorxiv_date < today + 2 days The dated form of biorxiv identifier has a start date of 2019-12-11. The Unix timestamp for that date is {{#time:U|2019-12-11}} = 1576022400 biorxiv_date is the date provided in those |biorxiv= parameter values that are dated at time 00:00:00 UTC today is the current date at time 00:00:00 UTC plus 48 hours if today is 2015-01-01T00:00:00 then adding 24 hours gives 2015-01-02T00:00:00 – one second more than today adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser apparently doesn't understand non-English date month names. This function will always return false when the date contains a non-English month name because good1 is false after the call to lang_object.formatDate(). To get around that call this function with date parts and create a YYYY-MM-DD format date. ]=] local function is_valid_biorxiv_date (y, m, d) local biorxiv_date = table.concat ({y, m, d}, '-'); -- make ymd date local good1, good2; local biorxiv_ts, tomorrow_ts; -- to hold Unix timestamps representing the dates local lang_object = mw.getContentLanguage(); good1, biorxiv_ts = pcall (lang_object.formatDate, lang_object, 'U', biorxiv_date); -- convert biorxiv_date value to Unix timestamp good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which tonumber() may not understand biorxiv_ts = tonumber (biorxiv_ts) or lang_object:parseFormattedNumber (biorxiv_ts); -- convert to numbers for the comparison; tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts); else return false; -- one or both failed to convert to Unix timestamp end return ((1576022400 <= biorxiv_ts) and (biorxiv_ts < tomorrow_ts)) -- 2012-12-11T00:00Z <= biorxiv_date < tomorrow's date end --[[--------------------------< IS _ V A L I D _ I S X N >----------------------------------------------------- ISBN-10 and ISSN validator code calculates checksum across all ISBN/ISSN digits including the check digit. ISBN-13 is checked in isbn(). If the number is valid the result will be 0. Before calling this function, ISBN/ISSN must be checked for length and stripped of dashes, spaces and other non-ISxN characters. ]] local function is_valid_isxn (isxn_str, len) local temp = 0; isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 len = len + 1; -- adjust to be a loop counter for i, v in ipairs (isxn_str) do -- loop through all of the bytes and calculate the checksum if v == string.byte ("X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) temp = temp + 10 * (len - i); -- it represents 10 decimal else temp = temp + tonumber (string.char (v) )*(len-i); end end return temp % 11 == 0; -- returns true if calculation result is zero end --[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >----------------------------------------------- ISBN-13 and ISMN validator code calculates checksum across all 13 ISBN/ISMN digits including the check digit. If the number is valid, the result will be 0. Before calling this function, ISBN-13/ISMN must be checked for length and stripped of dashes, spaces and other non-ISxN-13 characters. ]] local function is_valid_isxn_13 (isxn_str) local temp=0; isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 for i, v in ipairs (isxn_str) do temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit end return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct end --[[--------------------------< N O R M A L I Z E _ L C C N >-------------------------------------------------- LCCN normalization (https://www.loc.gov/marc/lccn-namespace.html#normalization) 1. Remove all blanks. 2. If there is a forward slash (/) in the string, remove it, and remove all characters to the right of the forward slash. 3. If there is a hyphen in the string: a. Remove it. b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out): 1. All these characters should be digits, and there should be six or less. (not done in this function) 2. If the length of the substring is less than 6, left-fill the substring with zeroes until the length is six. Returns a normalized LCCN for lccn() to validate. There is no error checking (step 3.b.1) performed in this function. ]] local function normalize_lccn (lccn) lccn = lccn:gsub ("%s", ""); -- 1. strip whitespace if nil ~= string.find (lccn, '/') then lccn = lccn:match ("(.-)/"); -- 2. remove forward slash and all character to the right of it end local prefix local suffix prefix, suffix = lccn:match ("(.+)%-(.+)"); -- 3.a remove hyphen by splitting the string into prefix and suffix if nil ~= suffix then -- if there was a hyphen suffix = string.rep("0", 6-string.len (suffix)) .. suffix; -- 3.b.2 left fill the suffix with 0s if suffix length less than 6 lccn = prefix..suffix; -- reassemble the LCCN end return lccn; end --============================<< I D E N T I F I E R F U N C T I O N S >>==================================== --[[--------------------------< A R X I V >-------------------------------------------------------------------- See: https://arxiv.org/help/arxiv_identifier format and error check arXiv identifier. There are three valid forms of the identifier: the first form, valid only between date codes 9107 and 0703, is: arXiv:<archive>.<class>/<date code><number><version> where: <archive> is a string of alpha characters - may be hyphenated; no other punctuation <class> is a string of alpha characters - may be hyphenated; no other punctuation; not the same as |class= parameter which is not supported in this form <date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01 first digit of YY for this form can only 9 and 0 <number> is a three-digit number <version> is a 1 or more digit number preceded with a lowercase v; no spaces (undocumented) the second form, valid from April 2007 through December 2014 is: arXiv:<date code>.<number><version> where: <date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01 <number> is a four-digit number <version> is a 1 or more digit number preceded with a lowercase v; no spaces the third form, valid from January 2015 is: arXiv:<date code>.<number><version> where: <date code> and <version> are as defined for 0704-1412 <number> is a five-digit number ]] local function arxiv (options) local id = options.id; local class = options.Class; -- TODO: lowercase? local handler = options.handler; local year, month, version; local err_msg = false; -- assume no error message local text; -- output text if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9107-0703 format with or without version year, month = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$"); year = tonumber (year); month = tonumber (month); if ((not (90 < year or 8 > year)) or (1 > month or 12 < month)) or -- if invalid year or invalid month ((91 == year and 7 > month) or (7 == year and 3 < month)) then -- if years ok, are starting and ending months ok? err_msg = true; -- flag for error message end elseif id:match("^%d%d[01]%d%.%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%dv%d+$") then -- test for the 0704-1412 with or without version year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$"); year = tonumber (year); month = tonumber (month); if ((7 > year) or (14 < year) or (1 > month or 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years) ((7 == year) and (4 > month)) then -- when year is 07, is month invalid (before April)? err_msg = true; -- flag for error message end elseif id:match("^%d%d[01]%d%.%d%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%d%dv%d+$") then -- test for the 1501- format with or without version year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d%d[v%d]*$"); year = tonumber (year); month = tonumber (month); if ((15 > year) or (1 > month or 12 < month)) then -- is year invalid or is month invalid? (doesn't test for future years) err_msg = true; -- flag for error message end else err_msg = true; -- not a recognized format; flag for error message end if err_msg then options.coins_list_t['ARXIV'] = nil; -- when error, unset so not included in COinS end local err_msg_t = {}; if err_msg then set_message ('err_bad_arxiv'); end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); if is_set (class) then if id:match ('^%d+') then text = table.concat ({text, ' [[//arxiv.org/archive/', class, ' ', class, ']]'}); -- external link within square brackets, not wikilink else set_message ('err_class_ignored'); end end return text; end --[[--------------------------< B I B C O D E >-------------------------------------------------------------------- Validates (sort of) and formats a bibcode ID. Format for bibcodes is specified here: https://adsabs.harvard.edu/abs_doc/help_pages/data.html#bibcodes But, this: 2015arXiv151206696F is apparently valid so apparently, the only things that really matter are length, 19 characters and first four digits must be a year. This function makes these tests: length must be 19 characters characters in position 1–4 must be digits and must represent a year in the range of 1000 – next year 5 must be a letter 6–8 must be letter, digit, ampersand, or dot (ampersand cannot directly precede a dot; &. ) 9–18 must be letter, digit, or dot 19 must be a letter or dot ]] local function bibcode (options) local id = options.id; local access = options.access; local handler = options.handler; local err_type; local err_msg = ''; local year; local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); if 19 ~= id:len() then err_type = cfg.err_msg_supl.length; else year = id:match ("^(%d%d%d%d)[%a][%w&%.][%w&%.][%w&%.][%w.]+[%a%.]$"); if not year then -- if nil then no pattern match err_type = cfg.err_msg_supl.value; -- so value error else local next_year = tonumber (os.date ('%Y')) + 1; -- get the current year as a number and add one for next year year = tonumber (year); -- convert year portion of bibcode to a number if (1000 > year) or (year > next_year) then err_type = cfg.err_msg_supl.year; -- year out of bounds end if id:find('&%.') then err_type = cfg.err_msg_supl.journal; -- journal abbreviation must not have '&.' (if it does it's missing a letter) end end end if is_set (err_type) then -- if there was an error detected set_message ('err_bad_bibcode', {err_type}); options.coins_list_t['BIBCODE'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< B I O R X I V >----------------------------------------------------------------- Format bioRxiv ID and do simple error checking. Before 2019-12-11, biorXiv IDs were 10.1101/ followed by exactly 6 digits. After 2019-12-11, biorXiv IDs retained the six-digit identifier but prefixed that with a yyyy.mm.dd. date and suffixed with an optional version identifier. The bioRxiv ID is the string of characters: https://doi.org/10.1101/078733 -> 10.1101/078733 or a date followed by a six-digit number followed by an optional version indicator 'v' and one or more digits: https://www.biorxiv.org/content/10.1101/2019.12.11.123456v2 -> 10.1101/2019.12.11.123456v2 see https://www.biorxiv.org/about-biorxiv ]] local function biorxiv (options) local id = options.id; local handler = options.handler; local err_msg = true; -- flag; assume that there will be an error local patterns = { '^10.1101/%d%d%d%d%d%d$', -- simple 6-digit identifier (before 2019-12-11) '^10.1101/(20[1-9]%d)%.([01]%d)%.([0-3]%d)%.%d%d%d%d%d%dv%d+$', -- y.m.d. date + 6-digit identifier + version (after 2019-12-11) '^10.1101/(20[1-9]%d)%.([01]%d)%.([0-3]%d)%.%d%d%d%d%d%d$', -- y.m.d. date + 6-digit identifier (after 2019-12-11) } for _, pattern in ipairs (patterns) do -- spin through the patterns looking for a match if id:match (pattern) then local y, m, d = id:match (pattern); -- found a match, attempt to get year, month and date from the identifier if m then -- m is nil when id is the six-digit form if not is_valid_biorxiv_date (y, m, d) then -- validate the encoded date; TODO: don't ignore leap-year and actual month lengths ({{#time:}} is a poor date validator) break; -- date fail; break out early so we don't unset the error message end end err_msg = nil; -- we found a match so unset the error message break; -- and done end end -- err_cat remains set here when no match if err_msg then options.coins_list_t['BIORXIV'] = nil; -- when error, unset so not included in COinS set_message ('err_bad_biorxiv'); -- and set the error message end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); end --[[--------------------------< C I T E S E E R X >------------------------------------------------------------ CiteSeerX use their own notion of "doi" (not to be confused with the identifiers resolved via doi.org). The description of the structure of this identifier can be found at Help_talk:Citation_Style_1/Archive_26#CiteSeerX_id_structure ]] local function citeseerx (options) local id = options.id; local handler = options.handler; local matched; local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); matched = id:match ("^10%.1%.1%.[1-9]%d?%d?%d?%.[1-9]%d?%d?%d?$"); if not matched then set_message ('err_bad_citeseerx' ); options.coins_list_t['CITESEERX'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< D O I >------------------------------------------------------------------------ Formats a DOI and checks for DOI errors. DOI names contain two parts: prefix and suffix separated by a forward slash. Prefix: directory indicator '10.' followed by a registrant code Suffix: character string of any length chosen by the registrant This function checks a DOI name for: prefix/suffix. If the DOI name contains spaces or endashes, or, if it ends with a period or a comma, this function will emit a bad_doi error message. DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash, and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely if ever used in DOI names. https://www.doi.org/doi_handbook/2_Numbering.html -- 2.2 Syntax of a DOI name https://www.doi.org/doi_handbook/2_Numbering.html#2.2.2 -- 2.2.2 DOI prefix ]] local function doi (options) local id = options.id; local inactive = options.DoiBroken local access = options.access; local ignore_invalid = options.accept; local handler = options.handler; local err_flag; local text; if is_set (inactive) then local inactive_year = inactive:match("%d%d%d%d") or ''; -- try to get the year portion from the inactive date local inactive_month, good; if is_set (inactive_year) then if 4 < inactive:len() then -- inactive date has more than just a year (could be anything) local lang_obj = mw.getContentLanguage(); -- get a language object for this wiki good, inactive_month = pcall (lang_obj.formatDate, lang_obj, 'F', inactive); -- try to get the month name from the inactive date if not good then inactive_month = nil; -- something went wrong so make sure this is unset end end else inactive_year = nil; -- |doi-broken-date= has something but it isn't a date end if is_set (inactive_year) and is_set (inactive_month) then set_message ('maint_doi_inactive_dated', {inactive_year, inactive_month, ' '}); elseif is_set (inactive_year) then set_message ('maint_doi_inactive_dated', {inactive_year, '', ''}); else set_message ('maint_doi_inactive'); end inactive = " (" .. cfg.messages['inactive'] .. ' ' .. inactive .. ')'; end local registrant = mw.ustring.match (id, '^10%.([^/]+)/[^%s–]-[^%.,]$'); -- registrant set when DOI has the proper basic form local registrant_err_patterns = { -- these patterns are for code ranges that are not supported '^[^1-3]%d%d%d%d%.%d%d*$', -- 5 digits with subcode (0xxxx, 40000+); accepts: 10000–39999 '^[^1-5]%d%d%d%d$', -- 5 digits without subcode (0xxxx, 60000+); accepts: 10000–59999 '^[^1-9]%d%d%d%.%d%d*$', -- 4 digits with subcode (0xxx); accepts: 1000–9999 '^[^1-9]%d%d%d$', -- 4 digits without subcode (0xxx); accepts: 1000–9999 '^%d%d%d%d%d%d+', -- 6 or more digits '^%d%d?%d?$', -- less than 4 digits without subcode (3 digits with subcode is legitimate) '^%d%d?%.[%d%.]+', -- 1 or 2 digits with subcode '^5555$', -- test registrant will never resolve '[^%d%.]', -- any character that isn't a digit or a dot } if not ignore_invalid then if registrant then -- when DOI has proper form for i, pattern in ipairs (registrant_err_patterns) do -- spin through error patterns if registrant:match (pattern) then -- to validate registrant codes err_flag = set_message ('err_bad_doi'); -- when found, mark this DOI as bad break; -- and done end end else err_flag = set_message ('err_bad_doi'); -- invalid directory or malformed end else set_message ('maint_doi_ignore'); end if err_flag then options.coins_list_t['DOI'] = nil; -- when error, unset so not included in COinS end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access, auto_link = not (err_flag or is_set (inactive) or ignore_invalid) and 'doi' or nil -- do not auto-link when |doi-broken-date= has a value or when there is a DOI error or (to play it safe, after all, auto-linking is not essential) when invalid DOIs are ignored }) .. (inactive or ''); return text; end --[[--------------------------< H D L >------------------------------------------------------------------------ Formats an HDL with minor error checking. HDL names contain two parts: prefix and suffix separated by a forward slash. Prefix: character string using any character in the UCS-2 character set except '/' Suffix: character string of any length using any character in the UCS-2 character set chosen by the registrant This function checks a HDL name for: prefix/suffix. If the HDL name contains spaces, endashes, or, if it ends with a period or a comma, this function will emit a bad_hdl error message. HDL names are case-insensitive and can incorporate any printable Unicode characters so the test for endashes and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely if ever used in HDLs. Query string parameters are named here: https://www.handle.net/proxy_servlet.html. query strings are not displayed but since '?' is an allowed character in an HDL, '?' followed by one of the query parameters is the only way we have to detect the query string so that it isn't URL-encoded with the rest of the identifier. ]] local function hdl (options) local id = options.id; local access = options.access; local handler = options.handler; local query_params = { -- list of known query parameters from https://www.handle.net/proxy_servlet.html 'noredirect', 'ignore_aliases', 'auth', 'cert', 'index', 'type', 'urlappend', 'locatt', 'action', } local hdl, suffix, param = id:match ('(.-)(%?(%a+).+)$'); -- look for query string local found; if hdl then -- when there are query strings, this is the handle identifier portion for _, q in ipairs (query_params) do -- spin through the list of query parameters if param:match ('^' .. q) then -- if the query string begins with one of the parameters found = true; -- announce a find break; -- and stop looking end end end if found then id = hdl; -- found so replace id with the handle portion; this will be URL-encoded, suffix will not else suffix = ''; -- make sure suffix is empty string for concatenation else end local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, suffix = suffix, separator = handler.separator, encode = handler.encode, access = access}) if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma set_message ('err_bad_hdl' ); options.coins_list_t['HDL'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< I S B N >---------------------------------------------------------------------- Determines whether an ISBN string is valid ]] local function isbn (options) local isbn_str = options.id; local ignore_invalid = options.accept; local handler = options.handler; local function return_result (check, err_type) -- local function to handle the various returns local ISBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, prefix = handler.prefix, id = isbn_str, separator = handler.separator}); if ignore_invalid then -- if ignoring ISBN errors set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error else -- here when not ignoring if not check then -- and there is an error options.coins_list_t['ISBN'] = nil; -- when error, unset so not included in COinS set_message ('err_bad_isbn', err_type); -- set an error message return ISBN; -- return id text end end return ISBN; -- return id text end if nil ~= isbn_str:match ('[^%s-0-9X]') then return return_result (false, cfg.err_msg_supl.char); -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X end local id = isbn_str:gsub ('[%s-]', ''); -- remove hyphens and whitespace local len = id:len(); if len ~= 10 and len ~= 13 then return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length end if len == 10 then if id:match ('^%d*X?$') == nil then -- fail if isbn_str has 'X' anywhere but last position return return_result (false, cfg.err_msg_supl.form); end if not is_valid_isxn (id, 10) then -- test isbn-10 for numerical validity return return_result (false, cfg.err_msg_supl.check); -- fail if isbn-10 is not numerically valid end if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin) return return_result (false, cfg.err_msg_supl.group); -- fail if isbn-10 begins with 630/1 end return return_result (true, cfg.err_msg_supl.check); -- pass if isbn-10 is numerically valid else if id:match ('^%d+$') == nil then return return_result (false, cfg.err_msg_supl.char); -- fail if ISBN-13 is not all digits end if id:match ('^97[89]%d*$') == nil then return return_result (false, cfg.err_msg_supl.prefix); -- fail when ISBN-13 does not begin with 978 or 979 end if id:match ('^9790') then return return_result (false, cfg.err_msg_supl.group); -- group identifier '0' is reserved to ISMN end return return_result (is_valid_isxn_13 (id), cfg.err_msg_supl.check); end end --[[--------------------------< A S I N >---------------------------------------------------------------------- Formats a link to Amazon. Do simple error checking: ASIN must be mix of 10 numeric or uppercase alpha characters. If a mix, first character must be uppercase alpha; if all numeric, ASINs must be 10-digit ISBN. If 10-digit ISBN, add a maintenance category so a bot or AWB script can replace |asin= with |isbn=. Error message if not 10 characters, if not ISBN-10, if mixed and first character is a digit. |asin=630....... and |asin=631....... are (apparently) not a legitimate ISBN though it checksums as one; these do not cause this function to emit the maint_asin message This function is positioned here because it calls isbn() ]] local function asin (options) local id = options.id; local domain = options.ASINTLD; local err_flag; if not id:match("^[%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$") then err_flag = set_message ('err_bad_asin'); -- ASIN is not a mix of 10 uppercase alpha and numeric characters else if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X) if is_valid_isxn (id, 10) then -- see if ASIN value is or validates as ISBN-10 if not id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not a valid isbn prefixes but are used by amazon as a numeric identifier err_flag = set_message ('err_bad_asin'); -- ASIN has ISBN-10 form but begins with something other than 630/1 so probably an isbn end elseif not is_set (err_flag) then err_flag = set_message ('err_bad_asin'); -- ASIN is not ISBN-10 end elseif not id:match("^%u[%d%u]+$") then err_flag = set_message ('err_bad_asin'); -- asin doesn't begin with uppercase alpha end end if (not is_set (domain)) or in_array (domain, {'us'}) then -- default: United States domain = "com"; elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom domain = "co." .. domain; elseif in_array (domain, {'z.cn'}) then -- China domain = "cn"; elseif in_array (domain, {'au', 'br', 'mx', 'sg', 'tr'}) then -- Australia, Brazil, Mexico, Singapore, Turkey domain = "com." .. domain; elseif not in_array (domain, {'ae', 'ca', 'cn', 'de', 'es', 'fr', 'in', 'it', 'nl', 'pl', 'sa', 'se', 'co.jp', 'co.uk', 'com', 'com.au', 'com.br', 'com.mx', 'com.sg', 'com.tr'}) then -- Arabic Emirates, Canada, China, Germany, Spain, France, Indonesia, Italy, Netherlands, Poland, Saudi Arabia, Sweden (as of 2021-03 Austria (.at), Liechtenstein (.li) and Switzerland (.ch) still redirect to the German site (.de) with special settings, so don't maintain local ASINs for them) err_flag = set_message ('err_bad_asin_tld'); -- unsupported asin-tld value end local handler = options.handler; if not is_set (err_flag) then options.coins_list_t['ASIN'] = handler.prefix .. domain .. "/dp/" .. id; -- asin for coins else options.coins_list_t['ASIN'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix .. domain .. "/dp/", id = id, encode = handler.encode, separator = handler.separator}) end --[[--------------------------< I S M N >---------------------------------------------------------------------- Determines whether an ISMN string is valid. Similar to ISBN-13, ISMN is 13 digits beginning 979-0-... and uses the same check digit calculations. See https://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf section 2, pages 9–12. ismn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx) or an identifier registered at info-uri.info (info:) ]] local function ismn (options) local id = options.id; local handler = options.handler; local text; local valid_ismn = true; local id_copy; id_copy = id; -- save a copy because this testing is destructive id = id:gsub ('[%s-]', ''); -- remove hyphens and white space if 13 ~= id:len() or id:match ("^9790%d*$" ) == nil then -- ISMN must be 13 digits and begin with 9790 valid_ismn = false; else valid_ismn=is_valid_isxn_13 (id); -- validate ISMN end -- text = internal_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- use this (or external version) when there is some place to link to -- prefix = handler.prefix, id = id_copy, separator = handler.separator, encode = handler.encode}) text = table.concat ( -- because no place to link to yet { make_wikilink (link_label_make (handler), handler.label), handler.separator, id_copy }); if false == valid_ismn then options.coins_list_t['ISMN'] = nil; -- when error, unset so not included in COinS; not really necessary here because ismn not made part of COinS set_message ('err_bad_ismn'); -- create an error message if the ISMN is invalid end return text; end --[[--------------------------< I S S N >---------------------------------------------------------------------- Validate and format an ISSN. This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four digits with a space. When that condition occurred, the resulting link looked like this: |issn=0819 4327 gives: [https://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link This code now prevents that by inserting a hyphen at the ISSN midpoint. It also validates the ISSN for length and makes sure that the checkdigit agrees with the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check ISSN error message. The ISSN is always displayed with a hyphen, even if the ISSN was given as a single group of 8 digits. ]] local function issn (options) local id = options.id; local handler = options.handler; local ignore_invalid = options.accept; local issn_copy = id; -- save a copy of unadulterated ISSN; use this version for display if ISSN does not validate local text; local valid_issn = true; id = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace if 8 ~= id:len() or nil == id:match ("^%d*X?$" ) then -- validate the ISSN: 8 digits long, containing only 0-9 or X in the last position valid_issn = false; -- wrong length or improper character else valid_issn = is_valid_isxn (id, 8); -- validate ISSN end if true == valid_issn then id = string.sub (id, 1, 4 ) .. "-" .. string.sub (id, 5 ); -- if valid, display correctly formatted version else id = issn_copy; -- if not valid, show the invalid ISSN with error message end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) if ignore_invalid then set_message ('maint_issn_ignore'); else if false == valid_issn then options.coins_list_t['ISSN'] = nil; -- when error, unset so not included in COinS set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or ''); -- create an error message if the ISSN is invalid end end return text; end --[[--------------------------< J F M >----------------------------------------------------------------------- A numerical identifier in the form nn.nnnn.nn ]] local function jfm (options) local id = options.id; local handler = options.handler; local id_num; id_num = id:match ('^[Jj][Ff][Mm](.*)$'); -- identifier with jfm prefix; extract identifier if is_set (id_num) then set_message ('maint_jfm_format'); else -- plain number without JFM prefix id_num = id; -- if here id does not have prefix end if id_num and id_num:match('^%d%d%.%d%d%d%d%.%d%d$') then id = id_num; -- jfm matches pattern else set_message ('err_bad_jfm' ); -- set an error message options.coins_list_t['JFM'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< J S T O R >-------------------------------------------------------------------- Format a JSTOR with some error checking ]] local function jstor (options) local id = options.id; local access = options.access; local handler = options.handler; if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then set_message ('err_bad_jstor'); -- set an error message options.coins_list_t['JSTOR'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); end --[[--------------------------< L C C N >---------------------------------------------------------------------- Format LCCN link and do simple error checking. LCCN is a character string 8-12 characters long. The length of the LCCN dictates the character type of the first 1-3 characters; the rightmost eight are always digits. https://oclc-research.github.io/infoURI-Frozen/info-uri.info/info:lccn/reg.html length = 8 then all digits length = 9 then lccn[1] is lowercase alpha length = 10 then lccn[1] and lccn[2] are both lowercase alpha or both digits length = 11 then lccn[1] is lower case alpha, lccn[2] and lccn[3] are both lowercase alpha or both digits length = 12 then lccn[1] and lccn[2] are both lowercase alpha ]] local function lccn (options) local lccn = options.id; local handler = options.handler; local err_flag; -- presume that LCCN is valid local id = lccn; -- local copy of the LCCN id = normalize_lccn (id); -- get canonical form (no whitespace, hyphens, forward slashes) local len = id:len(); -- get the length of the LCCN if 8 == len then if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) err_flag = set_message ('err_bad_lccn'); -- set an error message end elseif 9 == len then -- LCCN should be adddddddd if nil == id:match("%l%d%d%d%d%d%d%d%d") then -- does it match our pattern? err_flag = set_message ('err_bad_lccn'); -- set an error message end elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ... if nil == id:match("^%l%l%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern err_flag = set_message ('err_bad_lccn'); -- no match, set an error message end end elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd if not (id:match("^%l%l%l%d%d%d%d%d%d%d%d") or id:match("^%l%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns err_flag = set_message ('err_bad_lccn'); -- no match, set an error message end elseif 12 == len then -- LCCN should be aadddddddddd if not id:match("^%l%l%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern err_flag = set_message ('err_bad_lccn'); -- no match, set an error message end else err_flag = set_message ('err_bad_lccn'); -- wrong length, set an error message end if not is_set (err_flag) and nil ~= lccn:find ('%s') then err_flag = set_message ('err_bad_lccn'); -- lccn contains a space, set an error message end if is_set (err_flag) then options.coins_list_t['LCCN'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = lccn, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< M R >-------------------------------------------------------------------------- A seven digit number; if not seven digits, zero-fill leading digits to make seven digits. ]] local function mr (options) local id = options.id; local handler = options.handler; local id_num; local id_len; id_num = id:match ('^[Mm][Rr](%d+)$'); -- identifier with mr prefix if is_set (id_num) then set_message ('maint_mr_format'); -- add maint cat else -- plain number without mr prefix id_num = id:match ('^%d+$'); -- if here id is all digits end id_len = id_num and id_num:len() or 0; if (7 >= id_len) and (0 ~= id_len) then id = string.rep ('0', 7-id_len) .. id_num; -- zero-fill leading digits else set_message ('err_bad_mr'); -- set an error message options.coins_list_t['MR'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< O C L C >---------------------------------------------------------------------- Validate and format an OCLC ID. https://www.oclc.org/batchload/controlnumber.en.html {{dead link}} archived at: https://web.archive.org/web/20161228233804/https://www.oclc.org/batchload/controlnumber.en.html ]] local function oclc (options) local id = options.id; local handler = options.handler; local number; if id:match('^ocm%d%d%d%d%d%d%d%d$') then -- ocm prefix and 8 digits; 001 field (12 characters) number = id:match('ocm(%d+)'); -- get the number elseif id:match('^ocn%d%d%d%d%d%d%d%d%d$') then -- ocn prefix and 9 digits; 001 field (12 characters) number = id:match('ocn(%d+)'); -- get the number elseif id:match('^on%d%d%d%d%d%d%d%d%d%d+$') then -- on prefix and 10 or more digits; 001 field (12 characters) number = id:match('^on(%d%d%d%d%d%d%d%d%d%d+)$'); -- get the number elseif id:match('^%(OCoLC%)[1-9]%d*$') then -- (OCoLC) prefix and variable number digits; no leading zeros; 035 field number = id:match('%(OCoLC%)([1-9]%d*)'); -- get the number if 9 < number:len() then number = nil; -- constrain to 1 to 9 digits; change this when OCLC issues 10-digit numbers end elseif id:match('^%d+$') then -- no prefix number = id; -- get the number if 10 < number:len() then number = nil; -- constrain to 1 to 10 digits; change this when OCLC issues 11-digit numbers end end if number then -- proper format id = number; -- exclude prefix, if any, from external link else set_message ('err_bad_oclc') -- add an error message if the id is malformed options.coins_list_t['OCLC'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< O P E N L I B R A R Y >-------------------------------------------------------- Formats an OpenLibrary link, and checks for associated errors. ]] local function openlibrary (options) local id = options.id; local access = options.access; local handler = options.handler; local ident, code = id:gsub('^OL', ''):match("^(%d+([AMW]))$"); -- strip optional OL prefix followed immediately by digits followed by 'A', 'M', or 'W'; local err_flag; local prefix = { -- these are appended to the handler.prefix according to code ['A']='authors/OL', ['M']='books/OL', ['W']='works/OL', ['X']='OL' -- not a code; spoof when 'code' in id is invalid }; if not ident then code = 'X'; -- no code or id completely invalid ident = id; -- copy id to ident so that we display the flawed identifier err_flag = set_message ('err_bad_ol'); end if not is_set (err_flag) then options.coins_list_t['OL'] = handler.prefix .. prefix[code] .. ident; -- experiment for ol coins else options.coins_list_t['OL'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix .. prefix[code], id = ident, separator = handler.separator, encode = handler.encode, access = access}); end --[[--------------------------< O S T I >---------------------------------------------------------------------- Format OSTI and do simple error checking. OSTIs are sequential numbers beginning at 1 and counting up. This code checks the OSTI to see that it contains only digits and is less than test_limit specified in the configuration; the value in test_limit will need to be updated periodically as more OSTIs are issued. NB. 1018 is the lowest OSTI number found in the wild (so far) and resolving OK on the OSTI site ]] local function osti (options) local id = options.id; local access = options.access; local handler = options.handler; if id:match("[^%d]") then -- if OSTI has anything but digits set_message ('err_bad_osti'); -- set an error message options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS else -- OSTI is only digits local id_num = tonumber (id); -- convert id to a number for range testing if 1018 > id_num or handler.id_limit < id_num then -- if OSTI is outside test limit boundaries set_message ('err_bad_osti'); -- set an error message options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS end end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); end --[[--------------------------< P M C >------------------------------------------------------------------------ Format a PMC, do simple error checking, and check for embargoed articles. The embargo parameter takes a date for a value. If the embargo date is in the future the PMC identifier will not be linked to the article. If the embargo date is today or in the past, or if it is empty or omitted, then the PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix. PMC embargo date testing is done in function is_embargoed () which is called earlier because when the citation has |pmc=<value> but does not have a |url= then |title= is linked with the PMC link. Function is_embargoed () returns the embargo date if the PMC article is still embargoed, otherwise it returns an empty string. PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued. ]] local function pmc (options) local id = options.id; local embargo = options.Embargo; -- TODO: lowercase? local handler = options.handler; local err_flag; local id_num; local text; id_num = id:match ('^[Pp][Mm][Cc](%d+)$'); -- identifier with PMC prefix if is_set (id_num) then set_message ('maint_pmc_format'); else -- plain number without PMC prefix id_num = id:match ('^%d+$'); -- if here id is all digits end if is_set (id_num) then -- id_num has a value so test it id_num = tonumber (id_num); -- convert id_num to a number for range testing if 1 > id_num or handler.id_limit < id_num then -- if PMC is outside test limit boundaries err_flag = set_message ('err_bad_pmc'); -- set an error message else id = tostring (id_num); -- make sure id is a string end else -- when id format incorrect err_flag = set_message ('err_bad_pmc'); -- set an error message end if is_set (embargo) and is_set (is_embargoed (embargo)) then -- is PMC is still embargoed? text = table.concat ( -- still embargoed so no external link { make_wikilink (link_label_make (handler), handler.label), handler.separator, id, }); else text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- no embargo date or embargo has expired, ok to link to article prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access, auto_link = not err_flag and 'pmc' or nil -- do not auto-link when PMC has error }); end if err_flag then options.coins_list_t['PMC'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< P M I D >---------------------------------------------------------------------- Format PMID and do simple error checking. PMIDs are sequential numbers beginning at 1 and counting up. This code checks the PMID to see that it contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued. ]] local function pmid (options) local id = options.id; local handler = options.handler; if id:match("[^%d]") then -- if PMID has anything but digits set_message ('err_bad_pmid'); -- set an error message options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS else -- PMID is only digits local id_num = tonumber (id); -- convert id to a number for range testing if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries set_message ('err_bad_pmid'); -- set an error message options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS end end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --[[--------------------------< R F C >------------------------------------------------------------------------ Format RFC and do simple error checking. RFCs are sequential numbers beginning at 1 and counting up. This code checks the RFC to see that it contains only digits and is less than test_limit specified in the configuration; the value in test_limit will need to be updated periodically as more RFCs are issued. An index of all RFCs is here: https://tools.ietf.org/rfc/ ]] local function rfc (options) local id = options.id; local handler = options.handler; if id:match("[^%d]") then -- if RFC has anything but digits set_message ('err_bad_rfc'); -- set an error message options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS else -- RFC is only digits local id_num = tonumber (id); -- convert id to a number for range testing if 1 > id_num or handler.id_limit < id_num then -- if RFC is outside test limit boundaries set_message ('err_bad_rfc'); -- set an error message options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS end end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}); end --[[--------------------------< S 2 C I D >-------------------------------------------------------------------- Format an S2CID, do simple error checking S2CIDs are sequential numbers beginning at 1 and counting up. This code checks the S2CID to see that it is only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more S2CIDs are issued. ]] local function s2cid (options) local id = options.id; local access = options.access; local handler = options.handler; local id_num; local text; id_num = id:match ('^[1-9]%d*$'); -- id must be all digits; must not begin with 0; no open access flag if is_set (id_num) then -- id_num has a value so test it id_num = tonumber (id_num); -- convert id_num to a number for range testing if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries set_message ('err_bad_s2cid'); -- set an error message options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS end else -- when id format incorrect set_message ('err_bad_s2cid'); -- set an error message options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}); return text; end --[[--------------------------< S B N >------------------------------------------------------------------------ 9-digit form of ISBN-10; uses same check-digit validation when SBN is prefixed with an additional '0' to make 10 digits sbn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx) or an identifier registered at info-uri.info (info:) ]] local function sbn (options) local id = options.id; local ignore_invalid = options.accept; local handler = options.handler; local function return_result (check, err_type) -- local function to handle the various returns local SBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator}); if not ignore_invalid then -- if not ignoring SBN errors if not check then options.coins_list_t['SBN'] = nil; -- when error, unset so not included in COinS; not really necessary here because sbn not made part of COinS set_message ('err_bad_sbn', {err_type}); -- display an error message return SBN; end else set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error (ToDo: Possibly switch to separate message for SBNs only) end return SBN; end if id:match ('[^%s-0-9X]') then return return_result (false, cfg.err_msg_supl.char); -- fail if SBN contains anything but digits, hyphens, or the uppercase X end local ident = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace; they interfere with the rest of the tests if 9 ~= ident:len() then return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length end if ident:match ('^%d*X?$') == nil then return return_result (false, cfg.err_msg_supl.form); -- fail if SBN has 'X' anywhere but last position end return return_result (is_valid_isxn ('0' .. ident, 10), cfg.err_msg_supl.check); end --[[--------------------------< S S R N >---------------------------------------------------------------------- Format an SSRN, do simple error checking SSRNs are sequential numbers beginning at 100? and counting up. This code checks the SSRN to see that it is only digits and is greater than 99 and less than test_limit; the value in local variable test_limit will need to be updated periodically as more SSRNs are issued. ]] local function ssrn (options) local id = options.id; local handler = options.handler; local id_num; local text; id_num = id:match ('^%d+$'); -- id must be all digits if is_set (id_num) then -- id_num has a value so test it id_num = tonumber (id_num); -- convert id_num to a number for range testing if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries set_message ('err_bad_ssrn'); -- set an error message options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS end else -- when id format incorrect set_message ('err_bad_ssrn'); -- set an error message options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS end text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = options.access}); return text; end --[[--------------------------< U S E N E T _ I D >------------------------------------------------------------ Validate and format a usenet message id. Simple error checking, looks for 'id-left@id-right' not enclosed in '<' and/or '>' angle brackets. ]] local function usenet_id (options) local id = options.id; local handler = options.handler; local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) if not id:match('^.+@.+$') or not id:match('^[^<].*[^>]$') then -- doesn't have '@' or has one or first or last character is '< or '>' set_message ('err_bad_usenet_id') -- add an error message if the message id is invalid options.coins_list_t['USENETID'] = nil; -- when error, unset so not included in COinS end return text; end --[[--------------------------< Z B L >----------------------------------------------------------------------- A numerical identifier in the form nnnn.nnnnn - leading zeros in the first quartet optional format described here: http://emis.mi.sanu.ac.rs/ZMATH/zmath/en/help/search/ temporary format is apparently eight digits. Anything else is an error ]] local function zbl (options) local id = options.id; local handler = options.handler; if id:match('^%d%d%d%d%d%d%d%d$') then -- is this identifier using temporary format? set_message ('maint_zbl'); -- yes, add maint cat elseif not id:match('^%d?%d?%d?%d%.%d%d%d%d%d$') then -- not temporary, is it normal format? set_message ('err_bad_zbl'); -- no, set an error message options.coins_list_t['ZBL'] = nil; -- when error, unset so not included in COinS end return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}); end --============================<< I N T E R F A C E F U N C T I O N S >>========================================== --[[--------------------------< E X T R A C T _ I D S >------------------------------------------------------------ Populates ID table from arguments using configuration settings. Loops through cfg.id_handlers and searches args for any of the parameters listed in each cfg.id_handlers['...'].parameters. If found, adds the parameter and value to the identifier list. Emits redundant error message if more than one alias exists in args ]] local function extract_ids (args) local id_list = {}; -- list of identifiers found in args for k, v in pairs (cfg.id_handlers) do -- k is uppercase identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table v = select_one (args, v.parameters, 'err_redundant_parameters' ); -- v.parameters is a table of aliases for k; here we pick one from args if present if is_set (v) then id_list[k] = v; end -- if found in args, add identifier to our list end return id_list; end --[[--------------------------< E X T R A C T _ I D _ A C C E S S _ L E V E L S >-------------------------------------- Fetches custom id access levels from arguments using configuration settings. Parameters which have a predefined access level (e.g. arxiv) do not use this function as they are directly rendered as free without using an additional parameter. returns a table of k/v pairs where k is same as the identifier's key in cfg.id_handlers and v is the assigned (valid) keyword access-level values must match the case used in cfg.keywords_lists['id-access'] (lowercase unless there is some special reason for something else) ]] local function extract_id_access_levels (args, id_list) local id_accesses_list = {}; for k, v in pairs (cfg.id_handlers) do local access_param = v.custom_access; -- name of identifier's access-level parameter if is_set (access_param) then local access_level = args[access_param]; -- get the assigned value if there is one if is_set (access_level) then if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required set_message ('err_invalid_param_val', {access_param, access_level}); access_level = nil; -- invalid so unset end if not is_set (id_list[k]) then -- identifier access-level must have a matching identifier set_message ('err_param_access_requires_param', {k:lower()}); -- parameter name is uppercase in cfg.id_handlers (k); lowercase for error message end id_accesses_list[k] = cfg.keywords_xlate[access_level]; -- get translated keyword end end end return id_accesses_list; end --[[--------------------------< B U I L D _ I D _ L I S T >---------------------------------------------------- render the identifiers into a sorted sequence table <ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value <options_t> is a table of various k/v option pairs provided in the call to new_build_id_list(); modified by this function and passed to all identifier rendering functions <access_levels_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid) returns a sequence table of sorted (by hkey - 'handler' key) rendered identifier strings ]] local function build_id_list (ID_list_coins_t, options_t, access_levels_t) local ID_list_t = {}; local accept; local func_map = { --function map points to functions associated with hkey identifier ['ARXIV'] = arxiv, ['ASIN'] = asin, ['BIBCODE'] = bibcode, ['BIORXIV'] = biorxiv, ['CITESEERX'] = citeseerx, ['DOI'] = doi, ['EISSN'] = issn, ['HDL'] = hdl, ['ISBN'] = isbn, ['ISMN'] = ismn, ['ISSN'] = issn, ['JFM'] = jfm, ['JSTOR'] = jstor, ['LCCN'] = lccn, ['MR'] = mr, ['OCLC'] = oclc, ['OL'] = openlibrary, ['OSTI'] = osti, ['PMC'] = pmc, ['PMID'] = pmid, ['RFC'] = rfc, ['S2CID'] = s2cid, ['SBN'] = sbn, ['SSRN'] = ssrn, ['USENETID'] = usenet_id, ['ZBL'] = zbl, } for hkey, v in pairs (ID_list_coins_t) do v, accept = has_accept_as_written (v); -- remove accept-as-written markup if present; accept is boolean true when markup removed; false else -- every function gets the options table with value v and accept boolean options_t.hkey = hkey; -- ~/Configuration handler key options_t.id = v; -- add that identifier value to the options table options_t.accept = accept; -- add the accept boolean flag options_t.access = access_levels_t[hkey]; -- add the access level for those that have an |<identifier-access= parameter options_t.handler = cfg.id_handlers[hkey]; options_t.coins_list_t = ID_list_coins_t; -- pointer to ID_list_coins_t; for |asin= and |ol=; also to keep erroneous values out of the citation's metadata options_t.coins_list_t[hkey] = v; -- id value without accept-as-written markup for metadata if options_t.handler.access and not in_array (options_t.handler.access, cfg.keywords_lists['id-access']) then error (cfg.messages['unknown_ID_access'] .. options_t.handler.access); -- here when handler access key set to a value not listed in list of allowed id access keywords end if func_map[hkey] then local id_text = func_map[hkey] (options_t); -- call the function to get identifier text and any error message table.insert (ID_list_t, {hkey, id_text}); -- add identifier text to the output sequence table else error (cfg.messages['unknown_ID_key'] .. hkey); -- here when func_map doesn't have a function for hkey end end local function comp (a, b) -- used by following table.sort() return a[1]:lower() < b[1]:lower(); -- sort by hkey end table.sort (ID_list_t, comp); -- sequence table of tables sort for k, v in ipairs (ID_list_t) do -- convert sequence table of tables to simple sequence table of strings ID_list_t[k] = v[2]; -- v[2] is the identifier rendering from the call to the various functions in func_map{} end return ID_list_t; end --[[--------------------------< O P T I O N S _ C H E C K >---------------------------------------------------- check that certain option parameters have their associated identifier parameters with values <ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value <ID_support_t> is a sequence table of tables created in citation0() where each subtable has four elements: [1] is the support parameter's assigned value; empty string if not set [2] is a text string same as key in cfg.id_handlers [3] is cfg.error_conditions key used to create error message [4] is original ID support parameter name used to create error message returns nothing; on error emits an appropriate error message ]] local function options_check (ID_list_coins_t, ID_support_t) for _, v in ipairs (ID_support_t) do if is_set (v[1]) and not ID_list_coins_t[v[2]] then -- when support parameter has a value but matching identifier parameter is missing or empty set_message (v[3], (v[4])); -- emit the appropriate error message end end end --[[--------------------------< I D E N T I F I E R _ L I S T S _ G E T >-------------------------------------- Creates two identifier lists: a k/v table of identifiers and their values to be used locally and for use in the COinS metadata, and a sequence table of the rendered identifier strings that will be included in the rendered citation. ]] local function identifier_lists_get (args_t, options_t, ID_support_t) local ID_list_coins_t = extract_ids (args_t); -- get a table of identifiers and their values for use locally and for use in COinS options_check (ID_list_coins_t, ID_support_t); -- ID support parameters must have matching identifier parameters local ID_access_levels_t = extract_id_access_levels (args_t, ID_list_coins_t); -- get a table of identifier access levels local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t); -- get a sequence table of rendered identifier strings return ID_list_t, ID_list_coins_t; -- return the tables end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr, utilities_page_ptr) cfg = cfg_table_ptr; has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from select Module:Citation/CS1/Utilities module is_set = utilities_page_ptr.is_set; in_array = utilities_page_ptr.in_array; set_message = utilities_page_ptr.set_message; select_one = utilities_page_ptr.select_one; substitute = utilities_page_ptr.substitute; make_wikilink = utilities_page_ptr.make_wikilink; z = utilities_page_ptr.z; -- table of tables in Module:Citation/CS1/Utilities end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return { auto_link_urls = auto_link_urls, -- table of identifier URLs to be used when auto-linking |title= identifier_lists_get = identifier_lists_get, -- experiment to replace individual calls to build_id_list(), extract_ids, extract_id_access_levels is_embargoed = is_embargoed; set_selected_modules = set_selected_modules; } 7de1cb3ecf620ae52d26ff9beaf2d8b1c95dedca Module:Citation/CS1/COinS 828 402 805 804 2023-07-10T15:57:49Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1/COinS]] Scribunto text/plain --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local has_accept_as_written, is_set, in_array, remove_wiki_link, strip_apostrophe_markup; -- functions in Module:Citation/CS1/Utilities local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration --[[--------------------------< M A K E _ C O I N S _ T I T L E >---------------------------------------------- Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs) Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't corrupted with strings of %27%27... ]] local function make_coins_title (title, script) title = has_accept_as_written (title); if is_set (title) then title = strip_apostrophe_markup (title); -- strip any apostrophe markup else title = ''; -- if not set, make sure title is an empty string end if is_set (script) then script = script:gsub ('^%l%l%s*:%s*', ''); -- remove language prefix if present (script value may now be empty string) script = strip_apostrophe_markup (script); -- strip any apostrophe markup else script = ''; -- if not set, make sure script is an empty string end if is_set (title) and is_set (script) then script = ' ' .. script; -- add a space before we concatenate end return title .. script; -- return the concatenation end --[[--------------------------< E S C A P E _ L U A _ M A G I C _ C H A R S >---------------------------------- Returns a string where all of Lua's magic characters have been escaped. This is important because functions like string.gsub() treat their pattern and replace strings as patterns, not literal strings. ]] local function escape_lua_magic_chars (argument) argument = argument:gsub("%%", "%%%%"); -- replace % with %% argument = argument:gsub("([%^%$%(%)%.%[%]%*%+%-%?])", "%%%1"); -- replace all other Lua magic pattern characters return argument; end --[[--------------------------< G E T _ C O I N S _ P A G E S >------------------------------------------------ Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS. ]] local function get_coins_pages (pages) local pattern; if not is_set (pages) then return pages; end -- if no page numbers then we're done while true do pattern = pages:match("%[(%w*:?//[^ ]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the URL and following space(s): "[url " if nil == pattern then break; end -- no more URLs pattern = escape_lua_magic_chars (pattern); -- pattern is not a literal string; escape Lua's magic pattern characters pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible end pages = pages:gsub("[%[%]]", ""); -- remove the brackets pages = pages:gsub("–", "-" ); -- replace endashes with hyphens pages = pages:gsub("&%w+;", "-" ); -- and replace HTML entities (&ndash; etc.) with hyphens; do we need to replace numerical entities like &#32; and the like? return pages; end --[=[-------------------------< C O I N S _ R E P L A C E _ M A T H _ S T R I P M A R K E R >------------------ There are three options for math markup rendering that depend on the editor's math preference settings. These settings are at [[Special:Preferences#mw-prefsection-rendering]] and are PNG images TeX source MathML with SVG or PNG fallback All three are heavy with HTML and CSS which doesn't belong in the metadata. Without this function, the metadata saved in the raw wikitext contained the rendering determined by the settings of the last editor to save the page. This function gets the rendered form of an equation according to the editor's preference before the page is saved. It then searches the rendering for the text equivalent of the rendered equation and replaces the rendering with that so that the page is saved without extraneous HTML/CSS markup and with a reasonably readable text form of the equation. When a replacement is made, this function returns true and the value with replacement; otherwise false and the initial value. To replace multipe equations it is necessary to call this function from within a loop. ]=] local function coins_replace_math_stripmarker (value) local stripmarker = cfg.stripmarkers['math']; local rendering = value:match (stripmarker); -- is there a math stripmarker if not rendering then -- when value doesn't have a math stripmarker, abandon this test return false, value; end rendering = mw.text.unstripNoWiki (rendering); -- convert stripmarker into rendered value (or nil? ''? when math render error) if rendering:match ('alt="[^"]+"') then -- if PNG math option rendering = rendering:match ('alt="([^"]+)"'); -- extract just the math text elseif rendering:match ('$%s+.+%s+%$') then -- if TeX math option; $ is legit character that is escapes as \$ rendering = rendering:match ('$%s+(.+)%s+%$') -- extract just the math text elseif rendering:match ('<annotation[^>]+>.+</annotation>') then -- if MathML math option rendering = rendering:match ('<annotation[^>]+>(.+)</annotation>') -- extract just the math text else return false, value; -- had math stripmarker but not one of the three defined forms end return true, value:gsub (stripmarker, rendering, 1); end --[[--------------------------< C O I N S _ C L E A N U P >---------------------------------------------------- Cleanup parameter values for the metadata by removing or replacing invisible characters and certain HTML entities. 2015-12-10: there is a bug in mw.text.unstripNoWiki (). It replaces math stripmarkers with the appropriate content when it shouldn't. See https://phabricator.wikimedia.org/T121085 and Wikipedia_talk:Lua#stripmarkers_and_mw.text.unstripNoWiki.28.29 TODO: move the replacement patterns and replacement values into a table in /Configuration similar to the invisible characters table? ]] local function coins_cleanup (value) local replaced = true; -- default state to get the do loop running while replaced do -- loop until all math stripmarkers replaced replaced, value = coins_replace_math_stripmarker (value); -- replace math stripmarker with text representation of the equation end value = value:gsub (cfg.stripmarkers['math'], "MATH RENDER ERROR"); -- one or more couldn't be replaced; insert vague error message value = mw.text.unstripNoWiki (value); -- replace nowiki stripmarkers with their content value = value:gsub ('<span class="nowrap" style="padding%-left:0%.1em;">&#39;(s?)</span>', "'%1"); -- replace {{'}} or {{'s}} with simple apostrophe or apostrophe-s value = value:gsub ('&nbsp;', ' '); -- replace &nbsp; entity with plain space value = value:gsub ('\226\128\138', ' '); -- replace hair space with plain space if not mw.ustring.find (value, cfg.indic_script) then -- don't remove zero-width joiner characters from indic script value = value:gsub ('&zwj;', ''); -- remove &zwj; entities value = mw.ustring.gsub (value, '[\226\128\141\226\128\139\194\173]', ''); -- remove zero-width joiner, zero-width space, soft hyphen end value = value:gsub ('[\009\010\013 ]+', ' '); -- replace horizontal tab, line feed, carriage return with plain space return value; end --[[--------------------------< C O I N S >-------------------------------------------------------------------- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse the citation information. ]] local function COinS(data, class) if 'table' ~= type(data) or nil == next(data) then return ''; end for k, v in pairs (data) do -- spin through all of the metadata parameter values if 'ID_list' ~= k and 'Authors' ~= k then -- except the ID_list and Author tables (author nowiki stripmarker done when Author table processed) data[k] = coins_cleanup (v); end end local ctx_ver = "Z39.88-2004"; -- treat table strictly as an array with only set values. local OCinSoutput = setmetatable( {}, { __newindex = function(self, key, value) if is_set(value) then rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( remove_wiki_link( value ) ) } ); end end }); if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn', 'journal', 'news', 'magazine'}) or (in_array (class, {'conference', 'interview', 'map', 'press release', 'web'}) and is_set(data.Periodical)) or ('citation' == class and is_set(data.Periodical) and not is_set (data.Encyclopedia)) then OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; -- journal metadata identifier if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) then -- set genre according to the type of citation template we are rendering OCinSoutput["rft.genre"] = "preprint"; -- cite arxiv, cite biorxiv, cite citeseerx, cite ssrn elseif 'conference' == class then OCinSoutput["rft.genre"] = "conference"; -- cite conference (when Periodical set) elseif 'web' == class then OCinSoutput["rft.genre"] = "unknown"; -- cite web (when Periodical set) else OCinSoutput["rft.genre"] = "article"; -- journal and other 'periodical' articles end OCinSoutput["rft.jtitle"] = data.Periodical; -- journal only OCinSoutput["rft.atitle"] = data.Title; -- 'periodical' article titles -- these used only for periodicals OCinSoutput["rft.ssn"] = data.Season; -- keywords: winter, spring, summer, fall OCinSoutput["rft.quarter"] = data.Quarter; -- single digits 1->first quarter, etc. OCinSoutput["rft.chron"] = data.Chron; -- free-form date components OCinSoutput["rft.volume"] = data.Volume; -- does not apply to books OCinSoutput["rft.issue"] = data.Issue; OCinSoutput['rft.artnum'] = data.ArticleNumber; -- {{cite journal}} only OCinSoutput["rft.pages"] = data.Pages; -- also used in book metadata elseif 'thesis' ~= class then -- all others except cite thesis are treated as 'book' metadata; genre distinguishes OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; -- book metadata identifier if 'report' == class or 'techreport' == class then -- cite report and cite techreport OCinSoutput["rft.genre"] = "report"; elseif 'conference' == class then -- cite conference when Periodical not set OCinSoutput["rft.genre"] = "conference"; OCinSoutput["rft.atitle"] = data.Chapter; -- conference paper as chapter in proceedings (book) elseif in_array (class, {'book', 'citation', 'encyclopaedia', 'interview', 'map'}) then if is_set (data.Chapter) then OCinSoutput["rft.genre"] = "bookitem"; OCinSoutput["rft.atitle"] = data.Chapter; -- book chapter, encyclopedia article, interview in a book, or map title else if 'map' == class or 'interview' == class then OCinSoutput["rft.genre"] = 'unknown'; -- standalone map or interview else OCinSoutput["rft.genre"] = 'book'; -- book and encyclopedia end end else -- {'audio-visual', 'AV-media-notes', 'DVD-notes', 'episode', 'interview', 'mailinglist', 'map', 'newsgroup', 'podcast', 'press release', 'serial', 'sign', 'speech', 'web'} OCinSoutput["rft.genre"] = "unknown"; end OCinSoutput["rft.btitle"] = data.Title; -- book only OCinSoutput["rft.place"] = data.PublicationPlace; -- book only OCinSoutput["rft.series"] = data.Series; -- book only OCinSoutput["rft.pages"] = data.Pages; -- book, journal OCinSoutput["rft.edition"] = data.Edition; -- book only OCinSoutput["rft.pub"] = data.PublisherName; -- book and dissertation else -- cite thesis OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:dissertation"; -- dissertation metadata identifier OCinSoutput["rft.title"] = data.Title; -- dissertation (also patent but that is not yet supported) OCinSoutput["rft.degree"] = data.Degree; -- dissertation only OCinSoutput['rft.inst'] = data.PublisherName; -- book and dissertation end -- NB. Not currently supported are "info:ofi/fmt:kev:mtx:patent", "info:ofi/fmt:kev:mtx:dc", "info:ofi/fmt:kev:mtx:sch_svc", "info:ofi/fmt:kev:mtx:ctx" -- and now common parameters (as much as possible) OCinSoutput["rft.date"] = data.Date; -- book, journal, dissertation for k, v in pairs( data.ID_list ) do -- what to do about these? For now assume that they are common to all? if k == 'ISBN' then v = v:gsub( "[^-0-9X]", "" ); end local id = cfg.id_handlers[k].COinS; if string.sub( id or "", 1, 4 ) == 'info' then -- for ids that are in the info:registry OCinSoutput["rft_id"] = table.concat{ id, "/", v }; elseif string.sub (id or "", 1, 3 ) == 'rft' then -- for isbn, issn, eissn, etc. that have defined COinS keywords OCinSoutput[ id ] = v; elseif 'url' == id then -- for urls that are assembled in ~/Identifiers; |asin= and |ol= OCinSoutput["rft_id"] = table.concat ({data.ID_list[k], "#id-name=", cfg.id_handlers[k].label}); elseif id then -- when cfg.id_handlers[k].COinS is not nil so urls created here OCinSoutput["rft_id"] = table.concat{ cfg.id_handlers[k].prefix, v, cfg.id_handlers[k].suffix or '', "#id-name=", cfg.id_handlers[k].label }; -- others; provide a URL and indicate identifier name as #fragment (human-readable, but transparent to browsers) end end local last, first; for k, v in ipairs( data.Authors ) do last, first = coins_cleanup (v.last), coins_cleanup (v.first or ''); -- replace any nowiki stripmarkers, non-printing or invisible characters if k == 1 then -- for the first author name only if is_set(last) and is_set(first) then -- set these COinS values if |first= and |last= specify the first author name OCinSoutput["rft.aulast"] = last; -- book, journal, dissertation OCinSoutput["rft.aufirst"] = first; -- book, journal, dissertation elseif is_set(last) then OCinSoutput["rft.au"] = last; -- book, journal, dissertation -- otherwise use this form for the first name end else -- for all other authors if is_set(last) and is_set(first) then OCinSoutput["rft.au"] = table.concat{ last, ", ", first }; -- book, journal, dissertation elseif is_set(last) then OCinSoutput["rft.au"] = last; -- book, journal, dissertation end -- TODO: At present we do not report "et al.". Add anything special if this condition applies? end end OCinSoutput.rft_id = data.URL; OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage }; -- TODO: Add optional extra info: -- rfr_dat=#REVISION<version> (referrer private data) -- ctx_id=<data.RawPage>#<ref> (identifier for the context object) -- ctx_tim=<ts> (timestamp in format yyyy-mm-ddThh:mm:ssTZD or yyyy-mm-dd) -- ctx_enc=info:ofi/enc:UTF-8 (character encoding) OCinSoutput = setmetatable( OCinSoutput, nil ); -- sort with version string always first, and combine. -- table.sort( OCinSoutput ); table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004" return table.concat(OCinSoutput, "&"); end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr, utilities_page_ptr) cfg = cfg_table_ptr; has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from selected Module:Citation/CS1/Utilities module is_set = utilities_page_ptr.is_set; in_array = utilities_page_ptr.in_array; remove_wiki_link = utilities_page_ptr.remove_wiki_link; strip_apostrophe_markup = utilities_page_ptr.strip_apostrophe_markup; end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return { make_coins_title = make_coins_title, get_coins_pages = get_coins_pages, COinS = COinS, set_selected_modules = set_selected_modules, } 55b7d6a7605b5e672604b0210feeb5286b799f8e Module:Citation/CS1/styles.css 828 403 807 806 2023-07-10T15:57:50Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Citation/CS1/styles.css]] text text/plain /* Protection icon the following line controls the page-protection icon in the upper right corner it must remain within this comment {{sandbox other||{{pp-template}}}} */ /* Overrides Some wikis do not override user agent default styles for HTML <cite> and <q>, unlike en.wp. On en.wp, keep these the same as [[MediaWiki:Common.css]]. The word-wrap and :target styles were moved here from Common.css. On en.wp, keep these the same as [[Template:Citation/styles.css]]. */ cite.citation { font-style: inherit; /* Remove italics for <cite> */ /* Break long urls, etc., rather than overflowing box */ word-wrap: break-word; } .citation q { quotes: '"' '"' "'" "'"; /* Straight quote marks for <q> */ } /* Highlight linked elements (such as clicked references) in blue */ .citation:target { /* ignore the linter - all browsers of interest implement this */ background-color: rgba(0, 127, 255, 0.133); } /* ID and URL access Both core and Common.css have selector .mw-parser-output a[href$=".pdf"].external for PDF pages. All TemplateStyles pages are hoisted to .mw-parser-output. We need to have specificity equal to a[href$=".pdf"].external for locks to override PDF icon. That's essentially 2 classes and 1 element. the .id-lock-... selectors are for use by non-citation templates like {{Catalog lookup link}} which do not have to handle PDF links */ .id-lock-free a, .citation .cs1-lock-free a { background: url(//upload.wikimedia.org/wikipedia/commons/6/65/Lock-green.svg) right 0.1em center/9px no-repeat; } .id-lock-limited a, .id-lock-registration a, .citation .cs1-lock-limited a, .citation .cs1-lock-registration a { background: url(//upload.wikimedia.org/wikipedia/commons/d/d6/Lock-gray-alt-2.svg) right 0.1em center/9px no-repeat; } .id-lock-subscription a, .citation .cs1-lock-subscription a { background: url(//upload.wikimedia.org/wikipedia/commons/a/aa/Lock-red-alt-2.svg) right 0.1em center/9px no-repeat; } /* Wikisource Wikisource icon when |chapter= or |title= is wikilinked to Wikisource as in cite wikisource */ .cs1-ws-icon a { background: url(//upload.wikimedia.org/wikipedia/commons/4/4c/Wikisource-logo.svg) right 0.1em center/12px no-repeat; } /* Errors and maintenance */ .cs1-code { /* <code>...</code> style override: mediawiki's css definition is specified here: https://git.wikimedia.org/blob/mediawiki%2Fcore.git/ 69cd73811f7aadd093050dbf20ed70ef0b42a713/skins%2Fcommon%2FcommonElements.css#L199 */ color: inherit; background: inherit; border: none; padding: inherit; } .cs1-hidden-error { display: none; color: #d33; } .cs1-visible-error { color: #d33; } .cs1-maint { display: none; color: #3a3; margin-left: 0.3em; } /* Small text size Set small text size in one place. 0.95 (here) * 0.9 (from references list) is ~0.85, which is the lower bound for size for accessibility. Old styling for this was just 0.85. We could write the rule so that when this template is inside references/reflist, only then does it multiply by 0.95; else multiply by 0.85 */ .cs1-format { font-size: 95%; } /* kerning */ .cs1-kern-left { padding-left: 0.2em; } .cs1-kern-right { padding-right: 0.2em; } /* selflinks – avoid bold font style when cs1|2 template links to the current page */ .citation .mw-selflink { font-weight: inherit; } 7c96feb084b1883e7b6522660da6a14bdcc94752 Template:Pagetype 10 404 809 808 2023-07-10T15:57:51Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Pagetype]] wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:pagetype|main}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> b8e6aa66678cd57877ea2c607372a45070f030a7 Template:If both 10 405 811 810 2023-07-10T15:57:51Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:If_both]] wikitext text/x-wiki {{{{{|safesubst:}}}#if:{{{1|}}}| {{{{{|safesubst:}}}#if:{{{2|}}}|{{{3|}}}|{{{4|}}}}} |{{{4|}}} }}<noinclude> {{Documentation}} <!-- PLEASE ADD CATEGORIES AND INTERWIKIS TO THE /doc SUBPAGE, THANKS --> </noinclude> d77fc191cada8977a8131dd6d85dde5e31d0e6f2 Module:Pagetype 828 406 813 812 2023-07-10T15:57:52Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Pagetype]] Scribunto text/plain -------------------------------------------------------------------------------- -- -- -- PAGETYPE -- -- -- -- This is a meta-module intended to replace {{pagetype}} and similar -- -- templates. It automatically detects namespaces, and allows for a -- -- great deal of customisation. It can easily be ported to other -- -- wikis by changing the values in the [[Module:Pagetype/config]]. -- -- -- -------------------------------------------------------------------------------- -- Load config. local cfg = mw.loadData('Module:Pagetype/config') -- Load required modules. local getArgs = require('Module:Arguments').getArgs local yesno = require('Module:Yesno') local nsDetectModule = require('Module:Namespace detect') local nsDetect = nsDetectModule._main local getParamMappings = nsDetectModule.getParamMappings local getPageObject = nsDetectModule.getPageObject local p = {} local function shallowCopy(t) -- Makes a shallow copy of a table. local ret = {} for k, v in pairs(t) do ret[k] = v end return ret end local function checkPagetypeInput(namespace, val) -- Checks to see whether we need the default value for the given namespace, -- and if so gets it from the pagetypes table. -- The yesno function returns true/false for "yes", "no", etc., and returns -- val for other input. local ret = yesno(val, val) if ret and type(ret) ~= 'string' then ret = cfg.pagetypes[namespace] end return ret end local function getPagetypeFromClass(class, param, aliasTable, default) -- Gets the pagetype from a class specified from the first positional -- parameter. param = yesno(param, param) if param ~= false then -- No check if specifically disallowed. for _, alias in ipairs(aliasTable) do if class == alias then if type(param) == 'string' then return param else return default end end end end end local function getNsDetectValue(args) -- Builds the arguments to pass to [[Module:Namespace detect]] and returns -- the result. -- Get the default values. local ndArgs = {} local defaultns = args[cfg.defaultns] if defaultns == cfg.defaultnsAll then ndArgs = shallowCopy(cfg.pagetypes) else local defaultnsArray if defaultns == cfg.defaultnsExtended then defaultnsArray = cfg.extendedNamespaces elseif defaultns == cfg.defaultnsNone then defaultnsArray = {} else defaultnsArray = cfg.defaultNamespaces end for _, namespace in ipairs(defaultnsArray) do ndArgs[namespace] = cfg.pagetypes[namespace] end end --[[ -- Add custom values passed in from the arguments. These overwrite the -- defaults. The possible argument names are fetched from -- Module:Namespace detect automatically in case new namespaces are -- added. Although we accept namespace aliases as parameters, we only pass -- the local namespace name as a parameter to Module:Namespace detect. -- This means that the "image" parameter can overwrite defaults for the -- File: namespace, which wouldn't work if we passed the parameters through -- separately. --]] local mappings = getParamMappings() for ns, paramAliases in pairs(mappings) do -- Copy the aliases table, as # doesn't work with tables returned from -- mw.loadData. paramAliases = shallowCopy(paramAliases) local paramName = paramAliases[1] -- Iterate backwards along the array so that any values for the local -- namespace names overwrite those for namespace aliases. for i = #paramAliases, 1, -1 do local paramAlias = paramAliases[i] local ndArg = checkPagetypeInput(paramAlias, args[paramAlias]) if ndArg == false then -- If any arguments are false, convert them to nil to protect -- against breakage by future changes to -- [[Module:Namespace detect]]. ndArgs[paramName] = nil elseif ndArg then ndArgs[paramName] = ndArg end end end -- Check for disambiguation-class and N/A-class pages in mainspace. if ndArgs.main then local class = args[1] if type(class) == 'string' then -- Put in lower case so e.g. "Dab" and "dab" will both match. class = mw.ustring.lower(class) end local dab = getPagetypeFromClass( class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault ) if dab then ndArgs.main = dab else local na = getPagetypeFromClass( class, args[cfg.na], cfg.naAliases, cfg.naDefault ) if na then ndArgs.main = na end end end -- If there is no talk value specified, use the corresponding subject -- namespace for talk pages. if not ndArgs.talk then ndArgs.subjectns = true end -- Add the fallback value. This can also be customised, but it cannot be -- disabled. local other = args[cfg.other] -- We will ignore true/false/nil results from yesno here, but using it -- anyway for consistency. other = yesno(other, other) if type(other) == 'string' then ndArgs.other = other else ndArgs.other = cfg.otherDefault end -- Allow custom page values. ndArgs.page = args.page return nsDetect(ndArgs) end local function detectRedirects(args) local redirect = args[cfg.redirect] -- The yesno function returns true/false for "yes", "no", etc., and returns -- redirect for other input. redirect = yesno(redirect, redirect) if redirect == false then -- Detect redirects unless they have been explicitly disallowed with -- "redirect=no" or similar. return end local pageObject = getPageObject(args.page) -- If we are using subject namespaces elsewhere, do so here as well. if pageObject and not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll then pageObject = getPageObject( pageObject.subjectNsText .. ':' .. pageObject.text ) end -- Allow custom values for redirects. if pageObject and pageObject.isRedirect then if type(redirect) == 'string' then return redirect else return cfg.redirectDefault end end end function p._main(args) local redirect = detectRedirects(args) local pagetype = "" if redirect then pagetype = redirect else pagetype = getNsDetectValue(args) end if yesno(args.plural, false) then if cfg.irregularPlurals[pagetype] then pagetype = cfg.irregularPlurals[pagetype] else pagetype = pagetype .. cfg.plural -- often 's' end end if yesno(args.caps, false) then pagetype = mw.ustring.upper(mw.ustring.sub(pagetype, 1, 1)) .. mw.ustring.sub(pagetype, 2) end return pagetype end function p.main(frame) local args = getArgs(frame) return p._main(args) end return p 210524e0c60e3354325aea88c508e94423ad228d Module:Pagetype/config 828 407 815 814 2023-07-10T15:57:52Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Pagetype/config]] Scribunto text/plain -------------------------------------------------------------------------------- -- Module:Pagetype configuration data -- -- This page holds localisation and configuration data for Module:Pagetype. -- -------------------------------------------------------------------------------- local cfg = {} -- Don't edit this line. -------------------------------------------------------------------------------- -- Start configuration data -- -------------------------------------------------------------------------------- -- This table holds the values to use for "main=true", "user=true", etc. Keys to -- this table should be namespace parameters that can be used with -- [[Module:Namespace detect]]. cfg.pagetypes = { ['main'] = 'article', ['user'] = 'user page', ['project'] = 'project page', ['wikipedia'] = 'project page', ['wp'] = 'project page', ['file'] = 'file', ['image'] = 'file', ['mediawiki'] = 'interface page', ['template'] = 'template', ['help'] = 'help page', ['category'] = 'category', ['portal'] = 'portal', ['draft'] = 'draft', ['timedtext'] = 'Timed Text page', ['module'] = 'module', ['topic'] = 'topic', ['gadget'] = 'gadget', ['gadget definition'] = 'gadget definition', ['talk'] = 'talk page', ['special'] = 'special page', ['media'] = 'file', } -- This table holds the names of the namespaces to be looked up from -- cfg.pagetypes by default. cfg.defaultNamespaces = { 'main', 'file', 'template', 'category', 'module' } -- This table holds the names of the namespaces to be looked up from -- cfg.pagetypes if cfg.defaultnsExtended is set. cfg.extendedNamespaces = { 'main', 'user', 'project', 'file', 'mediawiki', 'template', 'category', 'help', 'portal', 'module', 'draft' } -- The parameter name to set which default namespace values to be looked up from -- cfg.pagetypes. cfg.defaultns = 'defaultns' -- The value of cfg.defaultns to set all namespaces, including talk. cfg.defaultnsAll = 'all' -- The value of cfg.defaultns to set the namespaces listed in -- cfg.extendedNamespaces cfg.defaultnsExtended = 'extended' -- The value of cfg.defaultns to set no default namespaces. cfg.defaultnsNone = 'none' -- The parameter name to use for disambiguation pages page. cfg.dab = 'dab' -- This table holds the different possible aliases for disambiguation-class -- pages. These should be lower-case. cfg.dabAliases = { 'disambiguation', 'disambig', 'disamb', 'dab' } -- The default value for disambiguation pages. cfg.dabDefault = 'page' -- The parameter name to use for N/A-class page. cfg.na = 'na' -- This table holds the different possible aliases for N/A-class pages. These -- should be lower-case. cfg.naAliases = {'na', 'n/a'} -- The default value for N/A-class pages. cfg.naDefault = 'page' -- The parameter name to use for redirects. cfg.redirect = 'redirect' -- The default value to use for redirects. cfg.redirectDefault = 'redirect' -- The parameter name for undefined namespaces. cfg.other = 'other' -- The value used if the module detects an undefined namespace. cfg.otherDefault = 'page' -- The usual suffix denoting a plural. cfg.plural = 's' -- This table holds plurals not formed by a simple suffix. cfg.irregularPlurals = { ["category"] = "categories" } -------------------------------------------------------------------------------- -- End configuration data -- -------------------------------------------------------------------------------- return cfg -- Don't edit this line e2eb36d6c43611a422bae37947ebeb04b695dcba Module:Namespace detect 828 408 817 816 2023-07-10T15:57:53Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Namespace_detect]] Scribunto text/plain --[[ -------------------------------------------------------------------------------- -- -- -- NAMESPACE DETECT -- -- -- -- This module implements the {{namespace detect}} template in Lua, with a -- -- few improvements: all namespaces and all namespace aliases are supported, -- -- and namespace names are detected automatically for the local wiki. The -- -- module can also use the corresponding subject namespace value if it is -- -- used on a talk page. Parameter names can be configured for different wikis -- -- by altering the values in the "cfg" table in -- -- Module:Namespace detect/config. -- -- -- -------------------------------------------------------------------------------- --]] local data = mw.loadData('Module:Namespace detect/data') local argKeys = data.argKeys local cfg = data.cfg local mappings = data.mappings local yesno = require('Module:Yesno') local mArguments -- Lazily initialise Module:Arguments local mTableTools -- Lazily initilalise Module:TableTools local ustringLower = mw.ustring.lower local p = {} local function fetchValue(t1, t2) -- Fetches a value from the table t1 for the first key in array t2 where -- a non-nil value of t1 exists. for i, key in ipairs(t2) do local value = t1[key] if value ~= nil then return value end end return nil end local function equalsArrayValue(t, value) -- Returns true if value equals a value in the array t. Otherwise -- returns false. for i, arrayValue in ipairs(t) do if value == arrayValue then return true end end return false end function p.getPageObject(page) -- Get the page object, passing the function through pcall in case of -- errors, e.g. being over the expensive function count limit. if page then local success, pageObject = pcall(mw.title.new, page) if success then return pageObject else return nil end else return mw.title.getCurrentTitle() end end -- Provided for backward compatibility with other modules function p.getParamMappings() return mappings end local function getNamespace(args) -- This function gets the namespace name from the page object. local page = fetchValue(args, argKeys.demopage) if page == '' then page = nil end local demospace = fetchValue(args, argKeys.demospace) if demospace == '' then demospace = nil end local subjectns = fetchValue(args, argKeys.subjectns) local ret if demospace then -- Handle "demospace = main" properly. if equalsArrayValue(argKeys.main, ustringLower(demospace)) then ret = mw.site.namespaces[0].name else ret = demospace end else local pageObject = p.getPageObject(page) if pageObject then if pageObject.isTalkPage then -- Get the subject namespace if the option is set, -- otherwise use "talk". if yesno(subjectns) then ret = mw.site.namespaces[pageObject.namespace].subject.name else ret = 'talk' end else ret = pageObject.nsText end else return nil -- return nil if the page object doesn't exist. end end ret = ret:gsub('_', ' ') return ustringLower(ret) end function p._main(args) -- Check the parameters stored in the mappings table for any matches. local namespace = getNamespace(args) or 'other' -- "other" avoids nil table keys local params = mappings[namespace] or {} local ret = fetchValue(args, params) --[[ -- If there were no matches, return parameters for other namespaces. -- This happens if there was no text specified for the namespace that -- was detected or if the demospace parameter is not a valid -- namespace. Note that the parameter for the detected namespace must be -- completely absent for this to happen, not merely blank. --]] if ret == nil then ret = fetchValue(args, argKeys.other) end return ret end function p.main(frame) mArguments = require('Module:Arguments') local args = mArguments.getArgs(frame, {removeBlanks = false}) local ret = p._main(args) return ret or '' end function p.table(frame) --[[ -- Create a wikitable of all subject namespace parameters, for -- documentation purposes. The talk parameter is optional, in case it -- needs to be excluded in the documentation. --]] -- Load modules and initialise variables. mTableTools = require('Module:TableTools') local namespaces = mw.site.namespaces local cfg = data.cfg local useTalk = type(frame) == 'table' and type(frame.args) == 'table' and yesno(frame.args.talk) -- Whether to use the talk parameter. -- Get the header names. local function checkValue(value, default) if type(value) == 'string' then return value else return default end end local nsHeader = checkValue(cfg.wikitableNamespaceHeader, 'Namespace') local aliasesHeader = checkValue(cfg.wikitableAliasesHeader, 'Aliases') -- Put the namespaces in order. local mappingsOrdered = {} for nsname, params in pairs(mappings) do if useTalk or nsname ~= 'talk' then local nsid = namespaces[nsname].id -- Add 1, as the array must start with 1; nsid 0 would be lost otherwise. nsid = nsid + 1 mappingsOrdered[nsid] = params end end mappingsOrdered = mTableTools.compressSparseArray(mappingsOrdered) -- Build the table. local ret = '{| class="wikitable"' .. '\n|-' .. '\n! ' .. nsHeader .. '\n! ' .. aliasesHeader for i, params in ipairs(mappingsOrdered) do for j, param in ipairs(params) do if j == 1 then ret = ret .. '\n|-' .. '\n| <code>' .. param .. '</code>' .. '\n| ' elseif j == 2 then ret = ret .. '<code>' .. param .. '</code>' else ret = ret .. ', <code>' .. param .. '</code>' end end end ret = ret .. '\n|-' .. '\n|}' return ret end return p a4757000273064f151f0f22dc0e139092e5ff443 Module:Namespace detect/data 828 409 819 818 2023-07-10T15:57:53Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Namespace_detect/data]] Scribunto text/plain -------------------------------------------------------------------------------- -- Namespace detect data -- -- This module holds data for [[Module:Namespace detect]] to be loaded per -- -- page, rather than per #invoke, for performance reasons. -- -------------------------------------------------------------------------------- local cfg = require('Module:Namespace detect/config') local function addKey(t, key, defaultKey) if key ~= defaultKey then t[#t + 1] = key end end -- Get a table of parameters to query for each default parameter name. -- This allows wikis to customise parameter names in the cfg table while -- ensuring that default parameter names will always work. The cfg table -- values can be added as a string, or as an array of strings. local defaultKeys = { 'main', 'talk', 'other', 'subjectns', 'demospace', 'demopage' } local argKeys = {} for i, defaultKey in ipairs(defaultKeys) do argKeys[defaultKey] = {defaultKey} end for defaultKey, t in pairs(argKeys) do local cfgValue = cfg[defaultKey] local cfgValueType = type(cfgValue) if cfgValueType == 'string' then addKey(t, cfgValue, defaultKey) elseif cfgValueType == 'table' then for i, key in ipairs(cfgValue) do addKey(t, key, defaultKey) end end cfg[defaultKey] = nil -- Free the cfg value as we don't need it any more. end local function getParamMappings() --[[ -- Returns a table of how parameter names map to namespace names. The keys -- are the actual namespace names, in lower case, and the values are the -- possible parameter names for that namespace, also in lower case. The -- table entries are structured like this: -- { -- [''] = {'main'}, -- ['wikipedia'] = {'wikipedia', 'project', 'wp'}, -- ... -- } --]] local mappings = {} local mainNsName = mw.site.subjectNamespaces[0].name mainNsName = mw.ustring.lower(mainNsName) mappings[mainNsName] = mw.clone(argKeys.main) mappings['talk'] = mw.clone(argKeys.talk) for nsid, ns in pairs(mw.site.subjectNamespaces) do if nsid ~= 0 then -- Exclude main namespace. local nsname = mw.ustring.lower(ns.name) local canonicalName = mw.ustring.lower(ns.canonicalName) mappings[nsname] = {nsname} if canonicalName ~= nsname then table.insert(mappings[nsname], canonicalName) end for _, alias in ipairs(ns.aliases) do table.insert(mappings[nsname], mw.ustring.lower(alias)) end end end return mappings end return { argKeys = argKeys, cfg = cfg, mappings = getParamMappings() } d224f42a258bc308ef3ad8cc8686cd7a4f47d005 Module:Namespace detect/config 828 410 821 820 2023-07-10T15:57:53Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Namespace_detect/config]] Scribunto text/plain -------------------------------------------------------------------------------- -- Namespace detect configuration data -- -- -- -- This module stores configuration data for Module:Namespace detect. Here -- -- you can localise the module to your wiki's language. -- -- -- -- To activate a configuration item, you need to uncomment it. This means -- -- that you need to remove the text "-- " at the start of the line. -- -------------------------------------------------------------------------------- local cfg = {} -- Don't edit this line. -------------------------------------------------------------------------------- -- Parameter names -- -- These configuration items specify custom parameter names. Values added -- -- here will work in addition to the default English parameter names. -- -- To add one extra name, you can use this format: -- -- -- -- cfg.foo = 'parameter name' -- -- -- -- To add multiple names, you can use this format: -- -- -- -- cfg.foo = {'parameter name 1', 'parameter name 2', 'parameter name 3'} -- -------------------------------------------------------------------------------- ---- This parameter displays content for the main namespace: -- cfg.main = 'main' ---- This parameter displays in talk namespaces: -- cfg.talk = 'talk' ---- This parameter displays content for "other" namespaces (namespaces for which ---- parameters have not been specified): -- cfg.other = 'other' ---- This parameter makes talk pages behave as though they are the corresponding ---- subject namespace. Note that this parameter is used with [[Module:Yesno]]. ---- Edit that module to change the default values of "yes", "no", etc. -- cfg.subjectns = 'subjectns' ---- This parameter sets a demonstration namespace: -- cfg.demospace = 'demospace' ---- This parameter sets a specific page to compare: cfg.demopage = 'page' -------------------------------------------------------------------------------- -- Table configuration -- -- These configuration items allow customisation of the "table" function, -- -- used to generate a table of possible parameters in the module -- -- documentation. -- -------------------------------------------------------------------------------- ---- The header for the namespace column in the wikitable containing the list of ---- possible subject-space parameters. -- cfg.wikitableNamespaceHeader = 'Namespace' ---- The header for the wikitable containing the list of possible subject-space ---- parameters. -- cfg.wikitableAliasesHeader = 'Aliases' -------------------------------------------------------------------------------- -- End of configuration data -- -------------------------------------------------------------------------------- return cfg -- Don't edit this line. 0e4ff08d13c4b664d66b32c232deb129b77c1a56 Template:Short description 10 411 823 822 2023-07-10T15:57:54Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Short_description]] wikitext text/x-wiki {{#ifeq:{{lc:{{{1|}}}}}|none|<nowiki /><!--Prevents whitespace issues when used with adjacent newlines-->|<div class="shortdescription nomobile noexcerpt noprint searchaux" style="display:none">{{{1|}}}{{SHORTDESC:{{{1|}}}|{{{2|}}}}}</div>}}<includeonly>{{#ifeq:{{{pagetype}}}|Disambiguation pages||{{#ifeq:{{pagetype |defaultns = all |user=exclude}}|exclude||{{#ifeq:{{#switch: {{NAMESPACENUMBER}} | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 100 | 101 | 118 | 119 | 828 | 829 | = exclude|#default=}}|exclude||[[Category:{{{pagetype|{{pagetype |defaultns = extended |plural=y}}}}} with short description]]}}}}}}</includeonly><!-- Start tracking -->{{#invoke:Check for unknown parameters|check|unknown={{Main other|[[Category:Pages using short description with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Short description]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | 2 | pagetype | bot |plural }}<!-- -->{{#ifexpr: {{#invoke:String|len|{{{1|}}}}}>100 | [[Category:{{{pagetype|{{pagetype |defaultns = extended |plural=y}}}}} with long short description]]}}<!-- --><includeonly>{{#if:{{{1|}}}||[[Category:Pages with empty short description]]}}</includeonly><!-- -->{{Short description/lowercasecheck|{{{1|}}}}}<!-- -->{{Main other |{{SDcat |sd={{{1|}}} }} }}<noinclude> {{Documentation}} </noinclude> f175a6d61b40a87adb43e2dd4f73c7979759b34c Template:Short description/lowercasecheck 10 412 825 824 2023-07-10T15:57:55Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Short_description/lowercasecheck]] wikitext text/x-wiki {{#ifeq:<!--test first character for lower-case letter-->{{#invoke:string|find|1={{{1|}}}|2=^%l|plain=false}}|1 |<!-- first character is a lower case letter; test against whitelist -->{{#switch: {{First word|{{{1|}}}}}<!--begin whitelist--> |c. <!--for circa--> |gTLD |iMac |iOS |iOS, |iPad |iPhone |iTunes |macOS |none |pH |pH-dependent=<!-- end whitelist; short description starts with an allowed lower-case string; whitelist matched; do nothing --> |#default=<!-- apply category to track lower-case short descriptions -->{{main other|[[Category:Pages with lower-case short description|{{trim|{{{1|}}}}}]]}}{{Testcases other|{{red|CATEGORY APPLIED}}}}<!-- end whitelist test -->}} |<!-- short description does not start with lower-case letter; do nothing; end lower-case test --> }}<noinclude> {{documentation}} </noinclude> 9a6d4db14b74614625fd234b4f8ee3c8e1a235c0 Template:SDcat 10 413 827 826 2023-07-10T15:57:55Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:SDcat]] wikitext text/x-wiki <includeonly>{{#invoke:SDcat |setCat}}</includeonly><noinclude> {{documentation}} </noinclude> 8c6e8783ddb0dc699d6fb60370db97b73725b9a6 Module:Check for unknown parameters 828 414 829 828 2023-07-10T15:57:56Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Check_for_unknown_parameters]] Scribunto text/plain -- This module may be used to compare the arguments passed to the parent -- with a list of arguments, returning a specified result if an argument is -- not on the list local p = {} local function trim(s) return s:match('^%s*(.-)%s*$') end local function isnotempty(s) return s and s:match('%S') end local function clean(text) -- Return text cleaned for display and truncated if too long. -- Strip markers are replaced with dummy text representing the original wikitext. local pos, truncated local function truncate(text) if truncated then return '' end if mw.ustring.len(text) > 25 then truncated = true text = mw.ustring.sub(text, 1, 25) .. '...' end return mw.text.nowiki(text) end local parts = {} for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do pos = remainder table.insert(parts, truncate(before) .. '&lt;' .. tag .. '&gt;...&lt;/' .. tag .. '&gt;') end table.insert(parts, truncate(text:sub(pos or 1))) return table.concat(parts) end function p._check(args, pargs) if type(args) ~= "table" or type(pargs) ~= "table" then -- TODO: error handling return end -- create the list of known args, regular expressions, and the return string local knownargs = {} local regexps = {} for k, v in pairs(args) do if type(k) == 'number' then v = trim(v) knownargs[v] = 1 elseif k:find('^regexp[1-9][0-9]*$') then table.insert(regexps, '^' .. v .. '$') end end -- loop over the parent args, and make sure they are on the list local ignoreblank = isnotempty(args['ignoreblank']) local showblankpos = isnotempty(args['showblankpositional']) local values = {} for k, v in pairs(pargs) do if type(k) == 'string' and knownargs[k] == nil then local knownflag = false for _, regexp in ipairs(regexps) do if mw.ustring.match(k, regexp) then knownflag = true break end end if not knownflag and ( not ignoreblank or isnotempty(v) ) then table.insert(values, clean(k)) end elseif type(k) == 'number' and knownargs[tostring(k)] == nil then local knownflag = false for _, regexp in ipairs(regexps) do if mw.ustring.match(tostring(k), regexp) then knownflag = true break end end if not knownflag and ( showblankpos or isnotempty(v) ) then table.insert(values, k .. ' = ' .. clean(v)) end end end -- add results to the output tables local res = {} if #values > 0 then local unknown_text = args['unknown'] or 'Found _VALUE_, ' if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then local preview_text = args['preview'] if isnotempty(preview_text) then preview_text = require('Module:If preview')._warning({preview_text}) elseif preview == nil then preview_text = unknown_text end unknown_text = preview_text end for _, v in pairs(values) do -- Fix odd bug for | = which gets stripped to the empty string and -- breaks category links if v == '' then v = ' ' end -- avoid error with v = 'example%2' ("invalid capture index") local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v}) table.insert(res, r) end end return table.concat(res) end function p.check(frame) local args = frame.args local pargs = frame:getParent().args return p._check(args, pargs) end return p 93db6d115d4328d2a5148bb42959105e367b663e Module:SDcat 828 415 831 830 2023-07-10T15:57:56Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:SDcat]] Scribunto text/plain --[[ SDcat Module to check whether local short description matches that on Wikidata --]] local p = {} ------------------------------------------------------------------------------- --[[ setCat has the qid of a Wikidata entity passed as |qid= (it defaults to the associated qid of the current article if omitted) and the local short description passed as |sd= It returns a category if there is an associated Wikidata entity. It returns one of the following tracking categories, as appropriate: * Category:Short description matches Wikidata (case-insensitive) * Category:Short description is different from Wikidata * Category:Short description with empty Wikidata description For testing purposes, a link prefix |lp= may be set to ":" to make the categories visible. --]] -- function exported for use in other modules -- (local short description, Wikidata entity-ID, link prefix) p._setCat = function(sdesc, itemID, lp) if not mw.wikibase then return nil end if itemID == "" then itemID = nil end -- Wikidata description field local wdesc = (mw.wikibase.getDescription(itemID) or ""):lower() if wdesc == "" then return "[[" .. lp .. "Category:Short description with empty Wikidata description]]" elseif wdesc == sdesc then return "[[" .. lp .. "Category:Short description matches Wikidata]]" else return "[[" .. lp .. "Category:Short description is different from Wikidata]]" end end -- function exported for call from #invoke p.setCat = function(frame) local args if frame.args.sd then args = frame.args else args = frame:getParent().args end -- local short description local sdesc = mw.text.trim(args.sd or ""):lower() -- Wikidata entity-ID local itemID = mw.text.trim(args.qid or "") -- link prefix, strip quotes local lp = mw.text.trim(args.lp or ""):gsub('"', '') return p._setCat(sdesc, itemID, lp) end return p 6c19ac0f72c79a618eb105808f74701376bb2b38 Module:InfoboxImage 828 416 833 832 2023-07-10T15:57:57Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:InfoboxImage]] Scribunto text/plain -- Inputs: -- image - Can either be a bare filename (with or without the File:/Image: prefix) or a fully formatted image link -- page - page to display for multipage images (DjVu) -- size - size to display the image -- maxsize - maximum size for image -- sizedefault - default size to display the image if size param is blank -- alt - alt text for image -- title - title text for image -- border - set to yes if border -- center - set to yes, if the image has to be centered -- upright - upright image param -- suppressplaceholder - if yes then checks to see if image is a placeholder and suppresses it -- link - page to visit when clicking on image -- class - HTML classes to add to the image -- Outputs: -- Formatted image. -- More details available at the "Module:InfoboxImage/doc" page local i = {}; local placeholder_image = { "Blue - Replace this image female.svg", "Blue - Replace this image male.svg", "Female no free image yet.png", "Flag of None (square).svg", "Flag of None.svg", "Flag of.svg", "Green - Replace this image female.svg", "Green - Replace this image male.svg", "Image is needed female.svg", "Image is needed male.svg", "Location map of None.svg", "Male no free image yet.png", "Missing flag.png", "No flag.svg", "No free portrait.svg", "No portrait (female).svg", "No portrait (male).svg", "Red - Replace this image female.svg", "Red - Replace this image male.svg", "Replace this image female (blue).svg", "Replace this image female.svg", "Replace this image male (blue).svg", "Replace this image male.svg", "Silver - Replace this image female.svg", "Silver - Replace this image male.svg", "Replace this image.svg", "Cricket no pic.png", "CarersLogo.gif", "Diagram Needed.svg", "Example.jpg", "Image placeholder.png", "No male portrait.svg", "Nocover-upload.png", "NoDVDcover copy.png", "Noribbon.svg", "No portrait-BFD-test.svg", "Placeholder barnstar ribbon.png", "Project Trains no image.png", "Image-request.png", "Sin bandera.svg", "Sin escudo.svg", "Replace this image - temple.png", "Replace this image butterfly.png", "Replace this image.svg", "Replace this image1.svg", "Resolution angle.png", "Image-No portrait-text-BFD-test.svg", "Insert image here.svg", "No image available.png", "NO IMAGE YET square.png", "NO IMAGE YET.png", "No Photo Available.svg", "No Screenshot.svg", "No-image-available.jpg", "Null.png", "PictureNeeded.gif", "Place holder.jpg", "Unbenannt.JPG", "UploadACopyrightFreeImage.svg", "UploadAnImage.gif", "UploadAnImage.svg", "UploadAnImageShort.svg", "CarersLogo.gif", "Diagram Needed.svg", "No male portrait.svg", "NoDVDcover copy.png", "Placeholder barnstar ribbon.png", "Project Trains no image.png", "Image-request.png", "Noimage.gif", } function i.IsPlaceholder(image) -- change underscores to spaces image = mw.ustring.gsub(image, "_", " "); assert(image ~= nil, 'mw.ustring.gsub(image, "_", " ") must not return nil') -- if image starts with [[ then remove that and anything after | if mw.ustring.sub(image,1,2) == "[[" then image = mw.ustring.sub(image,3); image = mw.ustring.gsub(image, "([^|]*)|.*", "%1"); assert(image ~= nil, 'mw.ustring.gsub(image, "([^|]*)|.*", "%1") must not return nil') end -- Trim spaces image = mw.ustring.gsub(image, '^[ ]*(.-)[ ]*$', '%1'); assert(image ~= nil, "mw.ustring.gsub(image, '^[ ]*(.-)[ ]*$', '%1') must not return nil") -- remove prefix if exists local allNames = mw.site.namespaces[6].aliases allNames[#allNames + 1] = mw.site.namespaces[6].name allNames[#allNames + 1] = mw.site.namespaces[6].canonicalName for i, name in ipairs(allNames) do if mw.ustring.lower(mw.ustring.sub(image, 1, mw.ustring.len(name) + 1)) == mw.ustring.lower(name .. ":") then image = mw.ustring.sub(image, mw.ustring.len(name) + 2); break end end -- Trim spaces image = mw.ustring.gsub(image, '^[ ]*(.-)[ ]*$', '%1'); -- capitalise first letter image = mw.ustring.upper(mw.ustring.sub(image,1,1)) .. mw.ustring.sub(image,2); for i,j in pairs(placeholder_image) do if image == j then return true end end return false end function i.InfoboxImage(frame) local image = frame.args["image"]; if image == "" or image == nil then return ""; end if image == "&nbsp;" then return image; end if frame.args["suppressplaceholder"] ~= "no" then if i.IsPlaceholder(image) == true then return ""; end end if mw.ustring.lower(mw.ustring.sub(image,1,5)) == "http:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,6)) == "[http:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,7)) == "[[http:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,6)) == "https:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,7)) == "[https:" then return ""; end if mw.ustring.lower(mw.ustring.sub(image,1,8)) == "[[https:" then return ""; end if mw.ustring.sub(image,1,2) == "[[" then -- search for thumbnail images and add to tracking cat if found local cat = ""; if mw.title.getCurrentTitle().namespace == 0 and (mw.ustring.find(image, "|%s*thumb%s*[|%]]") or mw.ustring.find(image, "|%s*thumbnail%s*[|%]]")) then cat = "[[Category:Pages using infoboxes with thumbnail images]]"; end return image .. cat; elseif mw.ustring.sub(image,1,2) == "{{" and mw.ustring.sub(image,1,3) ~= "{{{" then return image; elseif mw.ustring.sub(image,1,1) == "<" then return image; elseif mw.ustring.sub(image,1,5) == mw.ustring.char(127).."UNIQ" then -- Found strip marker at begining, so pass don't process at all return image; elseif mw.ustring.sub(image,4,9) == "`UNIQ-" then -- Found strip marker at begining, so pass don't process at all return image; else local result = ""; local page = frame.args["page"]; local size = frame.args["size"]; local maxsize = frame.args["maxsize"]; local sizedefault = frame.args["sizedefault"]; local alt = frame.args["alt"]; local link = frame.args["link"]; local title = frame.args["title"]; local border = frame.args["border"]; local upright = frame.args["upright"] or ""; local thumbtime = frame.args["thumbtime"] or ""; local center = frame.args["center"]; local class = frame.args["class"]; -- remove prefix if exists local allNames = mw.site.namespaces[6].aliases allNames[#allNames + 1] = mw.site.namespaces[6].name allNames[#allNames + 1] = mw.site.namespaces[6].canonicalName for i, name in ipairs(allNames) do if mw.ustring.lower(mw.ustring.sub(image, 1, mw.ustring.len(name) + 1)) == mw.ustring.lower(name .. ":") then image = mw.ustring.sub(image, mw.ustring.len(name) + 2); break end end if maxsize ~= "" and maxsize ~= nil then -- if no sizedefault then set to maxsize if sizedefault == "" or sizedefault == nil then sizedefault = maxsize end -- check to see if size bigger than maxsize if size ~= "" and size ~= nil then local sizenumber = tonumber(mw.ustring.match(size,"%d*")) or 0; local maxsizenumber = tonumber(mw.ustring.match(maxsize,"%d*")) or 0; if sizenumber>maxsizenumber and maxsizenumber>0 then size = maxsize; end end end -- add px to size if just a number if (tonumber(size) or 0) > 0 then size = size .. "px"; end -- add px to sizedefault if just a number if (tonumber(sizedefault) or 0) > 0 then sizedefault = sizedefault .. "px"; end result = "[[File:" .. image; if page ~= "" and page ~= nil then result = result .. "|page=" .. page; end if size ~= "" and size ~= nil then result = result .. "|" .. size; elseif sizedefault ~= "" and sizedefault ~= nil then result = result .. "|" .. sizedefault; else result = result .. "|frameless"; end if center == "yes" then result = result .. "|center" end if alt ~= "" and alt ~= nil then result = result .. "|alt=" .. alt; end if link ~= "" and link ~= nil then result = result .. "|link=" .. link; end if border == "yes" then result = result .. "|border"; end if upright == "yes" then result = result .. "|upright"; elseif upright ~= "" then result = result .. "|upright=" .. upright; end if thumbtime ~= "" then result = result .. "|thumbtime=" .. thumbtime; end if class ~= nil and class ~= "" then result = result .. "|class=" .. class; end -- if alt value is a keyword then do not use as a description if alt == "thumbnail" or alt == "thumb" or alt == "frameless" or alt == "left" or alt == "center" or alt == "right" or alt == "upright" or alt == "border" or mw.ustring.match(alt or "", '^[0-9]*px$', 1) ~= nil then alt = nil; end if title ~= "" and title ~= nil then -- does title param contain any templatestyles? If yes then set to blank. if mw.ustring.match(frame:preprocess(title), 'UNIQ%-%-templatestyles', 1) ~= nil then title = nil; end end if title ~= "" and title ~= nil then result = result .. "|" .. title; end result = result .. "]]"; return result; end end return i; 0ee5fe75ba239fc5c9cedc81ca11bdc0be068542 Module:Category handler 828 417 835 834 2023-07-10T15:57:58Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Category_handler]] Scribunto text/plain -------------------------------------------------------------------------------- -- -- -- CATEGORY HANDLER -- -- -- -- This module implements the {{category handler}} template in Lua, -- -- with a few improvements: all namespaces and all namespace aliases -- -- are supported, and namespace names are detected automatically for -- -- the local wiki. This module requires [[Module:Namespace detect]] -- -- and [[Module:Yesno]] to be available on the local wiki. It can be -- -- configured for different wikis by altering the values in -- -- [[Module:Category handler/config]], and pages can be blacklisted -- -- from categorisation by using [[Module:Category handler/blacklist]]. -- -- -- -------------------------------------------------------------------------------- -- Load required modules local yesno = require('Module:Yesno') -- Lazily load things we don't always need local mShared, mappings local p = {} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function trimWhitespace(s, removeBlanks) if type(s) ~= 'string' then return s end s = s:match('^%s*(.-)%s*$') if removeBlanks then if s ~= '' then return s else return nil end else return s end end -------------------------------------------------------------------------------- -- CategoryHandler class -------------------------------------------------------------------------------- local CategoryHandler = {} CategoryHandler.__index = CategoryHandler function CategoryHandler.new(data, args) local obj = setmetatable({ _data = data, _args = args }, CategoryHandler) -- Set the title object do local pagename = obj:parameter('demopage') local success, titleObj if pagename then success, titleObj = pcall(mw.title.new, pagename) end if success and titleObj then obj.title = titleObj if titleObj == mw.title.getCurrentTitle() then obj._usesCurrentTitle = true end else obj.title = mw.title.getCurrentTitle() obj._usesCurrentTitle = true end end -- Set suppression parameter values for _, key in ipairs{'nocat', 'categories'} do local value = obj:parameter(key) value = trimWhitespace(value, true) obj['_' .. key] = yesno(value) end do local subpage = obj:parameter('subpage') local category2 = obj:parameter('category2') if type(subpage) == 'string' then subpage = mw.ustring.lower(subpage) end if type(category2) == 'string' then subpage = mw.ustring.lower(category2) end obj._subpage = trimWhitespace(subpage, true) obj._category2 = trimWhitespace(category2) -- don't remove blank values end return obj end function CategoryHandler:parameter(key) local parameterNames = self._data.parameters[key] local pntype = type(parameterNames) if pntype == 'string' or pntype == 'number' then return self._args[parameterNames] elseif pntype == 'table' then for _, name in ipairs(parameterNames) do local value = self._args[name] if value ~= nil then return value end end return nil else error(string.format( 'invalid config key "%s"', tostring(key) ), 2) end end function CategoryHandler:isSuppressedByArguments() return -- See if a category suppression argument has been set. self._nocat == true or self._categories == false or ( self._category2 and self._category2 ~= self._data.category2Yes and self._category2 ~= self._data.category2Negative ) -- Check whether we are on a subpage, and see if categories are -- suppressed based on our subpage status. or self._subpage == self._data.subpageNo and self.title.isSubpage or self._subpage == self._data.subpageOnly and not self.title.isSubpage end function CategoryHandler:shouldSkipBlacklistCheck() -- Check whether the category suppression arguments indicate we -- should skip the blacklist check. return self._nocat == false or self._categories == true or self._category2 == self._data.category2Yes end function CategoryHandler:matchesBlacklist() if self._usesCurrentTitle then return self._data.currentTitleMatchesBlacklist else mShared = mShared or require('Module:Category handler/shared') return mShared.matchesBlacklist( self.title.prefixedText, mw.loadData('Module:Category handler/blacklist') ) end end function CategoryHandler:isSuppressed() -- Find if categories are suppressed by either the arguments or by -- matching the blacklist. return self:isSuppressedByArguments() or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist() end function CategoryHandler:getNamespaceParameters() if self._usesCurrentTitle then return self._data.currentTitleNamespaceParameters else if not mappings then mShared = mShared or require('Module:Category handler/shared') mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData end return mShared.getNamespaceParameters( self.title, mappings ) end end function CategoryHandler:namespaceParametersExist() -- Find whether any namespace parameters have been specified. -- We use the order "all" --> namespace params --> "other" as this is what -- the old template did. if self:parameter('all') then return true end if not mappings then mShared = mShared or require('Module:Category handler/shared') mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData end for ns, params in pairs(mappings) do for i, param in ipairs(params) do if self._args[param] then return true end end end if self:parameter('other') then return true end return false end function CategoryHandler:getCategories() local params = self:getNamespaceParameters() local nsCategory for i, param in ipairs(params) do local value = self._args[param] if value ~= nil then nsCategory = value break end end if nsCategory ~= nil or self:namespaceParametersExist() then -- Namespace parameters exist - advanced usage. if nsCategory == nil then nsCategory = self:parameter('other') end local ret = {self:parameter('all')} local numParam = tonumber(nsCategory) if numParam and numParam >= 1 and math.floor(numParam) == numParam then -- nsCategory is an integer ret[#ret + 1] = self._args[numParam] else ret[#ret + 1] = nsCategory end if #ret < 1 then return nil else return table.concat(ret) end elseif self._data.defaultNamespaces[self.title.namespace] then -- Namespace parameters don't exist, simple usage. return self._args[1] end return nil end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p = {} function p._exportClasses() -- Used for testing purposes. return { CategoryHandler = CategoryHandler } end function p._main(args, data) data = data or mw.loadData('Module:Category handler/data') local handler = CategoryHandler.new(data, args) if handler:isSuppressed() then return nil end return handler:getCategories() end function p.main(frame, data) data = data or mw.loadData('Module:Category handler/data') local args = require('Module:Arguments').getArgs(frame, { wrappers = data.wrappers, valueFunc = function (k, v) v = trimWhitespace(v) if type(k) == 'number' then if v ~= '' then return v else return nil end else return v end end }) return p._main(args, data) end return p b74dd63857b24904ac452429b11213f18647471f Module:Category handler/data 828 418 837 836 2023-07-10T15:57:59Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Category_handler/data]] Scribunto text/plain -- This module assembles data to be passed to [[Module:Category handler]] using -- mw.loadData. This includes the configuration data and whether the current -- page matches the title blacklist. local data = require('Module:Category handler/config') local mShared = require('Module:Category handler/shared') local blacklist = require('Module:Category handler/blacklist') local title = mw.title.getCurrentTitle() data.currentTitleMatchesBlacklist = mShared.matchesBlacklist( title.prefixedText, blacklist ) data.currentTitleNamespaceParameters = mShared.getNamespaceParameters( title, mShared.getParamMappings() ) return data abbc68048ff698e88dda06b64ecf384bbf583120 Module:Category handler/config 828 419 839 838 2023-07-10T15:57:59Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Category_handler/config]] Scribunto text/plain -------------------------------------------------------------------------------- -- [[Module:Category handler]] configuration data -- -- Language-specific parameter names and values can be set here. -- -- For blacklist config, see [[Module:Category handler/blacklist]]. -- -------------------------------------------------------------------------------- local cfg = {} -- Don't edit this line. -------------------------------------------------------------------------------- -- Start configuration data -- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- Parameter names -- -- These configuration items specify custom parameter names. -- -- To add one extra name, you can use this format: -- -- -- -- foo = 'parameter name', -- -- -- -- To add multiple names, you can use this format: -- -- -- -- foo = {'parameter name 1', 'parameter name 2', 'parameter name 3'}, -- -------------------------------------------------------------------------------- cfg.parameters = { -- The nocat and categories parameter suppress -- categorisation. They are used with Module:Yesno, and work as follows: -- -- cfg.nocat: -- Result of yesno() Effect -- true Categorisation is suppressed -- false Categorisation is allowed, and -- the blacklist check is skipped -- nil Categorisation is allowed -- -- cfg.categories: -- Result of yesno() Effect -- true Categorisation is allowed, and -- the blacklist check is skipped -- false Categorisation is suppressed -- nil Categorisation is allowed nocat = 'nocat', categories = 'categories', -- The parameter name for the legacy "category2" parameter. This skips the -- blacklist if set to the cfg.category2Yes value, and suppresses -- categorisation if present but equal to anything other than -- cfg.category2Yes or cfg.category2Negative. category2 = 'category2', -- cfg.subpage is the parameter name to specify how to behave on subpages. subpage = 'subpage', -- The parameter for data to return in all namespaces. all = 'all', -- The parameter name for data to return if no data is specified for the -- namespace that is detected. other = 'other', -- The parameter name used to specify a page other than the current page; -- used for testing and demonstration. demopage = 'page', } -------------------------------------------------------------------------------- -- Parameter values -- -- These are set values that can be used with certain parameters. Only one -- -- value can be specified, like this: -- -- -- -- cfg.foo = 'value name' -- -- -------------------------------------------------------------------------------- -- The following settings are used with the cfg.category2 parameter. Setting -- cfg.category2 to cfg.category2Yes skips the blacklist, and if cfg.category2 -- is present but equal to anything other than cfg.category2Yes or -- cfg.category2Negative then it supresses cateogrisation. cfg.category2Yes = 'yes' cfg.category2Negative = '¬' -- The following settings are used with the cfg.subpage parameter. -- cfg.subpageNo is the value to specify to not categorise on subpages; -- cfg.subpageOnly is the value to specify to only categorise on subpages. cfg.subpageNo = 'no' cfg.subpageOnly = 'only' -------------------------------------------------------------------------------- -- Default namespaces -- -- This is a table of namespaces to categorise by default. The keys are the -- -- namespace numbers. -- -------------------------------------------------------------------------------- cfg.defaultNamespaces = { [ 0] = true, -- main [ 6] = true, -- file [ 12] = true, -- help [ 14] = true, -- category [100] = true, -- portal [108] = true, -- book } -------------------------------------------------------------------------------- -- Wrappers -- -- This is a wrapper template or a list of wrapper templates to be passed to -- -- [[Module:Arguments]]. -- -------------------------------------------------------------------------------- cfg.wrappers = 'Template:Category handler' -------------------------------------------------------------------------------- -- End configuration data -- -------------------------------------------------------------------------------- return cfg -- Don't edit this line. 373cd107b13a5b00e6a1b7e66a749f12502c849d Module:Category handler/shared 828 420 841 840 2023-07-10T15:57:59Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Category_handler/shared]] Scribunto text/plain -- This module contains shared functions used by [[Module:Category handler]] -- and its submodules. local p = {} function p.matchesBlacklist(page, blacklist) for i, pattern in ipairs(blacklist) do local match = mw.ustring.match(page, pattern) if match then return true end end return false end function p.getParamMappings(useLoadData) local dataPage = 'Module:Namespace detect/data' if useLoadData then return mw.loadData(dataPage).mappings else return require(dataPage).mappings end end function p.getNamespaceParameters(titleObj, mappings) -- We don't use title.nsText for the namespace name because it adds -- underscores. local mappingsKey if titleObj.isTalkPage then mappingsKey = 'talk' else mappingsKey = mw.site.namespaces[titleObj.namespace].name end mappingsKey = mw.ustring.lower(mappingsKey) return mappings[mappingsKey] or {} end return p d2d5de1a031e6ce97c242cbfa8afe7a92cb9eca5 Module:Category handler/blacklist 828 421 843 842 2023-07-10T15:57:59Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Category_handler/blacklist]] Scribunto text/plain -- This module contains the blacklist used by [[Module:Category handler]]. -- Pages that match Lua patterns in this list will not be categorised unless -- categorisation is explicitly requested. return { '^Main Page$', -- don't categorise the main page. -- Don't categorise the following pages or their subpages. -- "%f[/\0]" matches if the next character is "/" or the end of the string. '^Wikipedia:Cascade%-protected items%f[/\0]', '^User:UBX%f[/\0]', -- The userbox "template" space. '^User talk:UBX%f[/\0]', -- Don't categorise subpages of these pages, but allow -- categorisation of the base page. '^Wikipedia:Template index/.*$', -- Don't categorise archives. '/[aA]rchive', "^Wikipedia:Administrators' noticeboard/IncidentArchive%d+$", } 87469d7a9ef2a3c41b2bf04ae18f7c59a18fb855 Template:Cite web 10 422 845 844 2023-07-10T15:58:00Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Cite_web]] wikitext text/x-wiki <includeonly>{{#invoke:citation/CS1|citation |CitationClass=web }}</includeonly><noinclude> {{documentation}} </noinclude> ea1b0f38afd9728a1cf9f2e3f540887a402fab8e Template:Reflist 10 423 847 846 2023-07-10T15:58:00Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Reflist]] wikitext text/x-wiki <templatestyles src="Reflist/styles.css" /><div class="reflist <!-- -->{{#if:{{{1|}}}{{{colwidth|}}}|reflist-columns references-column-width}} <!-- -->{{#switch:{{{liststyle|{{{group|}}}}}}|upper-alpha|upper-roman|lower-alpha|lower-greek|lower-roman=reflist-{{{liststyle|{{{group}}}}}}}} <!-- -->{{#if:{{{1|}}}|{{#iferror:{{#ifexpr: {{{1|1}}} > 1 }}||{{#switch:{{{1|}}}|1=|2=reflist-columns-2|#default=reflist-columns-3}} }}}}" <!-- end class -->{{#if: {{{1|}}}<!-- start style --> | {{#iferror: {{#ifexpr: {{{1|1}}} > 1 }} |style="column-width: {{{1}}};"}} | {{#if: {{{colwidth|}}}|style="column-width: {{{colwidth}}};"}} }}> {{#tag:references|{{{refs|}}}|group={{{group|}}}|responsive={{#if:{{{1|}}}{{{colwidth|}}}|0|1}}}}</div>{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using reflist with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Reflist]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | colwidth | group | liststyle | refs }}<noinclude> {{Documentation}} </noinclude> 8c65cc88272db6c0f5cf2b49f84d3e460e60ee5f Template:Reflist/styles.css 10 424 849 848 2023-07-10T15:58:01Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Reflist/styles.css]] text text/plain /* {{pp|small=yes}} */ /* can we remove the font size declarations? .references gets a font-size in * common.css that is always 90, and there is nothing else in reflist out in * the wild. May affect column sizes. */ .reflist { font-size: 90%; /* Default font-size */ margin-bottom: 0.5em; list-style-type: decimal; } .reflist .references { font-size: 100%; /* Reset font-size when nested in div.reflist */ margin-bottom: 0; /* Avoid double margin when nested in div.reflist */ list-style-type: inherit; /* Enable custom list style types */ } /* columns-2 and columns-3 are legacy for "2 or more" column view from when the * template was implemented with column-count. */ .reflist-columns-2 { column-width: 30em; } .reflist-columns-3 { column-width: 25em; } /* Reset top margin for lists embedded in columns */ .reflist-columns { margin-top: 0.3em; } .reflist-columns ol { margin-top: 0; } /* Avoid elements breaking between columns */ .reflist-columns li { page-break-inside: avoid; /* Removed from CSS in favor of break-inside c. 2020 */ break-inside: avoid-column; } .reflist-upper-alpha { list-style-type: upper-alpha; } .reflist-upper-roman { list-style-type: upper-roman; } .reflist-lower-alpha { list-style-type: lower-alpha; } .reflist-lower-greek { list-style-type: lower-greek; } .reflist-lower-roman { list-style-type: lower-roman; } 531a26d48f0e7826c61f764cfb7d5fb200032c34 Template:Find sources mainspace 10 425 851 850 2023-07-10T15:58:01Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Find_sources_mainspace]] wikitext text/x-wiki {{#invoke:Find sources|Find sources mainspace}}<noinclude> {{#invoke:Find sources/autodoc|Find sources mainspace}} </noinclude> 47c0ba9d406528e894f4077dd33ec0fe489d0af2 Module:Find sources 828 426 853 852 2023-07-10T15:58:02Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Find_sources]] Scribunto text/plain -- This module implements {{find sources}} and other similar templates, and -- also provides a mechanism to easily create new source-finding templates. -- Define constants local ROOT_PAGE = 'Module:Find sources' local TEMPLATE_ROOT = ROOT_PAGE .. '/templates/' -- for template config modules local LINK_CONFIG = ROOT_PAGE .. '/links' -- for link config modules local CONFIG_PAGE = ROOT_PAGE .. '/config' -- for global config -- Load required modules local checkType = require('libraryUtil').checkType local cfg = mw.loadData(CONFIG_PAGE) local p = {} local function maybeLoadData(page) local success, data = pcall(mw.loadData, page) return success and data end local function substituteParams(msg, ...) return mw.message.newRawMessage(msg, ...):plain() end local function renderSearchString(searchTerms, separator, transformFunc) -- This takes a table of search terms and turns it into a search string -- that can be used in a URL or in a display value. The transformFunc -- parameter can be used to transform each search term in some way (for -- example, URL-encoding them). local searchStrings = {} for i, s in ipairs(searchTerms) do searchStrings[i] = s end if transformFunc then for i, s in ipairs(searchStrings) do searchStrings[i] = transformFunc(s) end end return table.concat(searchStrings, separator) end function p._renderLink(code, searchTerms, display, tooltip) -- Renders the external link wikicode for one link, given the link code, -- a table of search terms, and an optional display value and tooltip. -- Get link config. local links = maybeLoadData(LINK_CONFIG) local linkCfg = links[code] if not linkCfg then error(string.format( "invalid link code '%s'; no link config found at [[%s]]", code, LINK_CONFIG )) end -- Make URL. local url do local separator = linkCfg.separator or "+" local searchString = renderSearchString( searchTerms, separator, mw.uri.encode ) url = substituteParams(linkCfg.url, searchString) end if tooltip then return string.format('<span title="%s" style="border-bottom: 1px dotted;">[%s %s]</span>', mw.text.encode(tooltip), url, display or linkCfg.display) else return string.format('[%s %s]', url, display or linkCfg.display) end end function p._main(template, args) -- The main access point from Lua. checkType('_main', 1, template, 'string') checkType('_main', 2, args, 'table', true) args = args or {} local title = mw.title.getCurrentTitle() -- Get the template config. local templateCfgPage = TEMPLATE_ROOT .. template local templateCfg = maybeLoadData(templateCfgPage) if not templateCfg then error(string.format( "invalid template name '%s'; no template config found at [[%s]]", template, templateCfgPage )) end -- Namespace check. if not templateCfg.isUsedInMainspace and title.namespace == 0 then local formatString = '<strong class="error">%s</strong>' if cfg['namespace-error-category'] then formatString = formatString .. '[[%s:%s]]' end return string.format( formatString, cfg['namespace-error'], mw.site.namespaces[14].name, cfg['namespace-error-category'] ) end -- Get the search terms from the arguments. local searchTerms = {} for i, s in ipairs(args) do searchTerms[i] = s end if not searchTerms[1] then -- Use the current subpage name as the default search term, unless -- another title is provided. If the page uses a disambiguator like -- "Foo (bar)", make "Foo" the first term and "bar" the second. local searchTitle = args.title or title.subpageText local term, dab = searchTitle:match('^(.*) (%b())$') if dab then dab = dab:sub(2, -2) -- Remove parens end if term and dab then searchTerms[1] = term searchTerms[2] = dab else searchTerms[1] = searchTitle end end searchTerms[1] = '"' .. searchTerms[1] .. '"' -- Make the intro link local introLink if templateCfg.introLink then local code = templateCfg.introLink.code local display = templateCfg.introLink.display or renderSearchString( searchTerms, '&nbsp;' ) local tooltip = templateCfg.introLink.tooltip introLink = p._renderLink(code, searchTerms, display, tooltip) else introLink = '' end -- Make the other links local links = {} local separator = templateCfg.separator or cfg['default-separator'] local sep = '' for i, t in ipairs(templateCfg.links) do links[i] = sep .. p._renderLink(t.code, searchTerms, t.display, t.tooltip) .. (t.afterDisplay or '') sep = t.separator or separator end links = table.concat(links) -- Make the blurb. local blurb = substituteParams(templateCfg.blurb, introLink, links) local span = mw.html.create('span') span :addClass('plainlinks') :addClass(templateCfg.class) :cssText(templateCfg.style) :wikitext(blurb) return tostring(span) end setmetatable(p, { __index = function(t, template) -- The main access point from #invoke. -- Invocations will look like {{#invoke:Find sources|template name}}, -- where "template name" is a subpage of [[Module:Find sources/templates]]. local tname = template if tname:sub(-8) == '/sandbox' then -- This makes {{Find sources/sandbox|Albert Einstein}} work. tname = tname:sub(1, -9) end return function(frame) local args = require('Module:Arguments').getArgs(frame, { wrappers = mw.site.namespaces[10].name .. ':' .. tname }) return t._main(template, args) end end}) return p 10c2d5c5a05295b49581eecba936dae039a4a290 Module:Find sources/config 828 427 855 854 2023-07-10T15:58:03Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Find_sources/config]] Scribunto text/plain -- Configuration data for [[Module:Find sources]]. return { -- Define the error message and category to be used if the module is used in -- the main namespace and the template config doesn't set the -- isUsedInMainspace key to true. The category is optional; if it is not -- wanted, it can be removed. ['namespace-error'] = 'Error: Please do not use this template in articles.', ['namespace-error-category'] = 'Pages with templates in the wrong namespace', -- The separator to be used if no separator is specified in the template -- config. ['default-separator'] = mw.message.new('Dot-separator'):plain() } 412ea800b842be74cd74e8289e346e8b30b1df91 Module:Find sources/templates/Find sources mainspace 828 428 857 856 2023-07-10T15:58:04Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Find_sources/templates/Find_sources_mainspace]] Scribunto text/plain return { blurb = "''Find sources:''&nbsp;$1&nbsp;–&nbsp;$2", introLink = { code = 'google' }, links = { { code = 'google news', display = 'news' }, { code = 'google newspapers', display = 'newspapers' }, { code = 'google books', display = 'books' }, { code = 'google scholar', display = 'scholar' }, { code = 'jstor', display = 'JSTOR' } }, isUsedInMainspace = true } abfeb1a778b232887c9400ca7667092141e60257 Module:Find sources/links 828 429 859 858 2023-07-10T15:58:04Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Find_sources/links]] Scribunto text/plain -- This is a list of links used by [[Module:Find sources]]. return { ["archive.org"] = { url = 'https://archive.org/search.php?query=$1%20AND%20mediatype:texts', display = 'Archive.org', description = "The [[Internet Archive]], a digital library of public websites.", }, ["bing"] = { url = 'https://www.bing.com/search?q=$1', display = 'Bing', description = "[[Bing (search engine)|Bing]], Microsoft's flagship search engine.", }, ["britannica"] = { url = 'https://www.britannica.com/search?nop=1a15&cse=on&query=$1&cx=ccef96e8363da4b5f&tbm=3&fxx=3', display = 'Encyclopedia Britannica', description = "[[Encyclopedia Britannica]]", }, ["british library"] = { url = 'https://explore.bl.uk/primo_library/libweb/action/search.do?fn=search&ct=search&initialSearch=true&mode=Basic&tab=local_tab&indx=1&dum=true&srt=rank&vid=BLVU1&frbg=&tb=t&vl%28freeText0%29=$1', display = 'British Library', description = "National library of the United Kingdom", }, ["ccsearch"] = { url = "https://search.creativecommons.org/search?q=$1&license_type=commercial,modification", display = "CC Search", description = "CC Search: The official search engine of [[Creative Commons]]", }, ["cochrane"] = { url = "https://www.cochranelibrary.com/en/advanced-search?searchBy=-1&isWordVariations=&resultPerPage=25&searchType=advanced&selectedType=review&displayText=&orderBy=relevancy&p_p_id=scolarissearchresultsportlet_WAR_scolarissearchresults&p_p_lifecycle=0&p_p_state=normal&p_p_mode=view&p_p_col_id=column-1&p_p_col_pos=1&p_p_col_count=2&searchText=$1", display = "Cochrane", description = "[[Cochrane Library]]: Leading publisher of systematic reviews.", }, ["doaj"] = { url = "https://www.doaj.org/search/articles?source=%7B%22query%22%3A%7B%22query_string%22%3A%7B%22query%22%3A$1%2C%22default_operator%22%3A%22AND%22%7D%7D%7D", display = "DOAJ", description = "[[Directory of Open Access Journals|DOAJ]]: Directory of Open Access Journals", }, ["duckduckgo"] = { url = 'https://duckduckgo.com/?q=$1', display = 'DuckDuckGo', description = "[[DuckDuckGo]], a search engine that emphasizes protecting searchers' privacy and avoiding the \"filter bubble\" of personalized search results.", }, ["eowb"] = { url = 'https://search.lib.umich.edu/everything?query=$1', display = 'Encyclopedia of World Biography', tooltip = 'Encyclopedia of World Biography, by Gale Research; online results provided by University of Michigan.', }, ["free news sources"] = { url = 'https://en.wikipedia.org/wiki/Wikipedia:Free_English_newspaper_sources', display = 'free news sources', }, ["gale"] = { url = "https://go.gale.com/ps/basicSearch.do?inputFieldNames%5B0%5D=OQE&nwf=y&searchType=BasicSearchForm&userGroupName=anon%7Ee3e4f4eb&prodId=AONE&spellCheck=true&method=doSearch&dblist=&stw.option=&ebook=&singleLimiterFieldValues%5BAC%5D=y&_singleLimiterFieldValues%5BAC%5D=on&_singleLimiterFieldValues%5BRE%5D=on&standAloneLimiters=LI&_singleLimiterFieldValues%5BLI%5D=on&inputFieldValues%5B0%5D=$1", display = "Gale Academic OneFile", description = " Gale Academic OneFile: Academic publisher portal from [[Gale (publisher)|Gale]]. Access via [[Wikipedia:The Wikipedia Library|Wikipedia Library]]", }, ["gin"] = { url = "https://guidelines.ebmportal.com/guidelines-international-network?type=search&search=$1", display = "GIN guidelines", tooltip = "Content from the library and the registry of guidelines in development provided by the Guidelines International Network", }, ["globe and mail"] = { url = 'https://www.theglobeandmail.com/search/?q=$1', display = "''The Globe and Mail''", description = "The website of ''[[The Globe and Mail]]'', a [[newspaper of record]] for Canada.", }, ["google"] = { url = 'https://www.google.com/search?as_eq=wikipedia&q=$1', display = 'Google', description = "[[Google]], the flagship search engine from Google Inc.", }, ["google books"] = { url = 'https://www.google.com/search?tbs=bks:1&q=$1+-wikipedia', display = 'Google Books', description = "[[Google Books]], Google's search engine for books." }, ["google free images"] = { url = 'https://www.google.com/search?safe=off&tbs=sur:fmc&tbm=isch&q=$1+-site:wikipedia.org+-site:wikimedia.org', display = 'Free Google Images', description = "[[Google Images]], Google's search engine for images. Only images compatible with Wikipedia's licensing are included.", }, ["google news"] = { url = 'https://www.google.com/search?tbm=nws&q=$1+-wikipedia&tbs=ar:1', display = 'Google News', description = "[[Google News]], Google's search engine for news sites.", notes = "In the past this link searched news archives, but this functionality has been removed by Google. Currently, only recent news articles are searched.", }, ["google newspapers"] = { url = 'https://www.google.com/search?&q=$1&tbs=bkt:s&tbm=bks', display = 'Google Newspapers', description = "Google Newspapers, a search of Google's digital archive of print newspapers.", }, ["google scholar"] = { url = 'https://scholar.google.com/scholar?q=$1', display = 'Google Scholar', description = "[[Google Scholar]], Google's search engine for academic papers and other scholarly research.", }, ["haaretz"] = { url = 'https://www.haaretz.com/search-results?q=$1', display = "''Haaretz''", description = "The website of ''[[Haaretz]]'', a [[newspaper of record]] for Israel.", }, ["internet archive scholar"] = { url = 'https://scholar.archive.org/search?&sort_order=time_desc&q=$1', display = 'IA scholar', description = "The [[Internet Archive Scholar]], a digital library of open access academic journals.", tooltip = "The [[Internet Archive Scholar]], a digital library of open access academic journals.", }, ["infoplease"] = { url = 'https://www.infoplease.com/search/$1', display = 'Infoplease', tooltip = 'Infoplease encyclopedia, dictionary, and almanac', }, ["jstor"] = { url = 'https://www.jstor.org/action/doBasicSearch?Query=$1&acc=on&wc=on', display = 'JSTOR', description = "[[JSTOR]], an online library containing digitised versions of academic journals. Requires a subscription." }, ["library of congress"] = { url = 'https://www.loc.gov/search/?in=&q=$1', display = "Library of Congress", description = "U.S. [[Library of Congress]]", }, ["mail and guardian"] = { url = 'https://mg.co.za/?s=$1', display = "''Mail & Guardian''", description = "The website of the ''[[Mail & Guardian]]'', a [[newspaper of record]] for South Africa.", }, ["medrs"] = { url = "https://en.wikipedia.org/wiki/Wikipedia:Identifying_reliable_sources_(medicine)", display = "find medical sources", tooltip = "Ideal sources for biomedical material include recent literature reviews and medical guidelines; learn how to identify reliable sources for medical content at Wikipedia at WP:MEDRS.", description = "[[WP:MEDRS|MEDRS]]: Ideal sources for biomedical material include recent literature reviews and medical guidelines; learn how to identify reliable sources for medical content.", }, ["muse"] = { url = 'https://muse.jhu.edu/search?action=search&min=1&max=10&t=header&query=content:$1:and', display = 'MUSE', tooltip = 'Project MUSE: humanities and social science content from academic journals and societies.', }, ["new york times"] = { url = 'https://www.nytimes.com/search/$1', display = "''New York Times''", description = "The website of ''[[The New York Times]]'', a [[newspaper of record]] for the United States.", }, ["new zealand herald"] = { url = 'https://www.nzherald.co.nz/search/$1/', display = "''The New Zealand Herald''", description = "The website of ''[[The New Zealand Herald]]'', a [[newspaper of record]] for New Zealand.", }, ["openlibrary"] = { url = 'https://openlibrary.org/search?q=$1&mode=everything', display = 'OpenLibrary', separator = '+', }, ["openmd"] = { url = "https://openmd.com/search?q=$1", display = "OpenMD", description = "OpenMD: Search engine for medical literature.", }, ["pubmed"] = { url = "https://pubmed.ncbi.nlm.nih.gov/?term=$1&filter=pubt.meta-analysis&filter=pubt.review&filter=pubt.systematicreview&filter=datesearch.y_10", display = "PubMed", description = "[[PubMed]]: Search engine for biomedical literature from [[United States National Library of Medicine|NLM]].", }, ["sciencedirect"] = { url = "https://www.sciencedirect.com/search?qs=$1&articleTypes=REV%2CEN%2CCH%2CSSU%2CPGL&lastSelectedFacet=articleTypes", display = "ScienceDirect", description = "ScienceDirect: [[Elsevier]]'s scientific, technical, and medical research portal.", }, ["south china morning post"] = { url = 'https://www.scmp.com/search/$1', display = "''South China Morning Post''", description = "The website of the ''[[South China Morning Post]]'', a [[newspaper of record]] for Hong Kong.", }, ["springer"] = { url = "https://link.springer.com/search?query=$1", display = "Springer", description = "Springer Nature's portal for journals, books, and reference works.", }, ["statpearls"] = { url = "https://www.ncbi.nlm.nih.gov/books/NBK430685/?term=$1", display = "StatPearls", description = "StatPearls: the largest provider of healthcare continuing education worldwide, providing peer-reviewed practice-guiding knowledge authored by clinical experts.", }, ["straits times"] = { url = 'https://www.straitstimes.com/search?searchkey=$1', display = "''The Straits Times''", description = "The website of ''[[The Straits Times]]'', a [[newspaper of record]] for Singapore.", }, ["sydney morning herald"] = { url = 'https://www.smh.com.au/search?text=$1', display = "''The Sydney Morning Herald''", description = "The website of ''[[The Sydney Morning Herald]]'', a [[newspaper of record]] for Australia.", }, ["the age"] = { url = 'https://www.theage.com.au/search?text=$1', display = "''The Age''", description = "The website of ''[[The Age]]'', a [[newspaper of record]] for Australia.", }, ["the guardian"] = { url = 'https://www.google.co.uk/search?as_sitesearch=www.theguardian.com&q=$1', display = 'The Guardian', description = "''[[The Guardian]]'' newspaper, U.K.", }, ["the hindu"] = { url = 'https://www.thehindu.com/search/?q=$1', display = "''The Hindu''", description = "The website of ''[[The Hindu]]'', a [[newspaper of record]] for India.", }, ["the times"] = { url = 'https://www.thetimes.co.uk/search?source=search-page&q=$1', display = "''The Times''", description = "The website of ''[[The Times]]'', a [[newspaper of record]] for the United Kingdom.", }, ["times of india"] = { url = 'https://timesofindia.indiatimes.com/topic/$1', display = "''The Times of India''", description = "The website of ''[[The Times of India]]'', a [[newspaper of record]] for India.", }, ["trip"] = { url = "https://www.tripdatabase.com/Searchresult?search_type=standard&criteria=$1&from_date=2012", display = "Trip Database", description = "Trip Database: Search engine for clinical research evidence.", }, ["uptodate"] = { url = "https://www.uptodate.com/contents/search?sp=0&searchType=PLAIN_TEXT&source=USER_INPUT&searchControl=TOP_PULLDOWN&searchOffset=1&autoComplete=false&language=en&max=10&search=$1", display = "UpToDate", tooltip = "Evidence-based resource for clinical decision support written for and by physicians", }, ["vgrl"] = { url = 'https://en.wikipedia.org/wiki/Special:Search?search=$1&prefix=Wikipedia%3AWikiProject+Video+games%2FReference+library&fulltext=Search+reference+library&fulltext=Search', display = 'VGRL', description = "[[Wikipedia:WikiProject Video games/Reference library]] internal archive search.", }, ["vgrs"] = { url = 'https://www.google.com/cse?cx=009782238053898643791%3A8naerdbd-oy&q=$1', display = 'VGRS', description = "[http://www.google.com/cse/home?cx=003516479746865699832:leawcwkqifq Google RS], a [[custom Google search engine]] that limits the search to sites listed in [[Wikipedia:WikiProject Video games/Sources]].", }, ["vgtalk"] = { url = 'https://en.wikipedia.org/w/index.php?search=$1+prefix%3AWikipedia+talk%3AWikiProject+Video+games&title=Special:Search&profile=default&fulltext=1', display = 'WPVG Talk', description = "A search in the WikiProject Video games talk page and its archives under [[Wikipedia talk:WikiProject Video games]].", }, ["wikipedia library"] = { url = "https://wikipedialibrary.wmflabs.org/search/?q=$1", display = "TWL", tooltip = "The Wikipedia Library", description = "Search results from dozens of services provided via EBSCOhost to Wikipedians via [[Wikipedia:The Wikipedia Library]].", }, ["wikipedia reference search"] = { url = 'https://www.google.com/custom?hl=en&cx=007734830908295939403%3Agalkqgoksq0&cof=FORID%3A13%3BAH%3Aleft%3BCX%3AWikipedia%2520Reference%2520Search&q=$1', display = 'Wikipedia Reference Search', description = "[[WP:WRS|Wikipedia Reference Search]], a Google search that only searches sites vetted by Wikipedians.", }, ["wiley"] = { url = "https://onlinelibrary.wiley.com/action/doSearch?AllField=$1", display = "Wiley", description = "Wiley Online Library: [[Wiley (publisher)|Wiley's]] portal for academic articles, books, and collections.", }, } 4817c288e68e87936971b4b7bb22926dc62c114c Template:PAGENAMEBASE 10 430 861 860 2023-07-10T15:58:05Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:PAGENAMEBASE]] wikitext text/x-wiki {{{{{|safesubst:}}}#Invoke:String|replace|{{{1|{{{{{|safesubst:}}}PAGENAME}}}}}|%s+%b()$||1|false}}<noinclude> {{documentation}} </noinclude> f23a5d434cb5b0baac5e1f58e9ceef9118e6873f Module:If preview 828 431 863 862 2023-07-10T15:58:05Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:If_preview]] Scribunto text/plain local p = {} local cfg = mw.loadData('Module:If preview/configuration') --[[ main This function returns either the first argument or second argument passed to this module, depending on whether the page is being previewed. ]] function p.main(frame) if cfg.preview then return frame.args[1] or '' else return frame.args[2] or '' end end --[[ pmain This function returns either the first argument or second argument passed to this module's parent (i.e. template using this module), depending on whether it is being previewed. ]] function p.pmain(frame) return p.main(frame:getParent()) end local function warning_text(warning) return mw.ustring.format( cfg.warning_infrastructure, cfg.templatestyles, warning ) end function p._warning(args) local warning = args[1] and args[1]:match('^%s*(.-)%s*$') or '' if warning == '' then return warning_text(cfg.missing_warning) end if not cfg.preview then return '' end return warning_text(warning) end --[[ warning This function returns a "preview warning", which is the first argument marked up with HTML and some supporting text, depending on whether the page is being previewed. disabled since we'll implement the template version in general ]] --function p.warning(frame) -- return p._warning(frame.args) --end --[[ warning, but for pass-through templates like {{preview warning}} ]] function p.pwarning(frame) return p._warning(frame:getParent().args) end return p 9a92196d0001b8016f2501aedfadcc3adcb974ef Module:If preview/configuration 828 432 865 864 2023-07-10T15:58:06Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:If_preview/configuration]] Scribunto text/plain --[[ We perform the actual check for whether this is a preview here since preprocessing is relatively expensive. ]] local frame = mw.getCurrentFrame() local function is_preview() local revision_id = frame:preprocess('{{REVISIONID}}') -- {{REVISIONID}} is usually the empty string when previewed. -- I don't know why we're checking for nil but hey, maybe someday things -- would have broken return revision_id == nil or revision_id == '' end local function templatestyles() return frame:extensionTag{ name = 'templatestyles', args = { src = 'Module:If preview/styles.css' } } end return { preview = is_preview(), templatestyles = templatestyles(), warning_infrastructure = '%s<div class="preview-warning"><strong>Preview warning:</strong> %s</div>', missing_warning = 'The template has no warning text. Please add a warning.' } 3edc8897c51a61b9e710b2a4d9eb657b3c2f1034 Module:If preview/styles.css 828 433 867 866 2023-07-10T15:58:06Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:If_preview/styles.css]] text text/plain /* {{pp|small=yes}} */ .preview-warning { font-style: italic; /* @noflip */ padding-left: 1.6em; margin-bottom: 0.5em; color: red; } /* The templatestyles element inserts a link element before hatnotes. * TODO: Remove link if/when WMF resolves T200206 */ .preview-warning + link + .preview-warning { margin-top: -0.5em; } 8b79ffc4853d424a805b084de00030e04bbd573e Template:Cite magazine 10 434 869 868 2023-07-10T15:58:06Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Cite_magazine]] wikitext text/x-wiki <includeonly>{{#invoke:Citation/CS1|citation |CitationClass=magazine }}</includeonly><noinclude> {{documentation}} </noinclude> 9e3a497b787ae19d8cb5e2085b21bb4bfc02eee0 Template:Icon 10 435 871 870 2023-07-10T15:58:07Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Icon]] wikitext text/x-wiki {{#invoke:Icon|main}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> bd5b855953c5eec9d9c48400aa39315cb4218558 Template:Navbox 10 436 873 872 2023-07-10T15:58:07Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Navbox]] wikitext text/x-wiki <includeonly>{{#invoke:Navbox|navbox}}</includeonly><noinclude> {{Documentation}} </noinclude> fe9b964401f895918ee4fe078678f1722a3c41ec Module:Icon 828 437 875 874 2023-07-10T15:58:07Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Icon]] Scribunto text/plain -- This module implements [[Template:Icon]]. require("strict") local yesNo = require("Module:Yesno") local getArgs = require("Module:Arguments").getArgs local getPlain = nil local p = {} -- Determine whether we're being called from a sandbox local sandbox = mw.getCurrentFrame():getTitle():find('sandbox', 1, true) and '/sandbox' or '' -- Implements [[Template:Icon]] -- Returns the icon image corresponding to a string (like 'B') function p._main(args, data) local data_module = 'Module:Icon/data'..sandbox data = data or mw.loadData(data_module) local code = args.class or args[1] local iconData if code then code = code:match('^%s*(.-)%s*$'):lower() -- trim whitespace and put in lower case iconData = data[code] end if not iconData then iconData = data._DEFAULT end return string.format( '[[File:%s%s%s|%s|class=noviewer|alt=%s]]', iconData.image, iconData.tooltip and '|' .. iconData.tooltip or '', iconData.link == false and '|link=' or '', args.size or '16x16px', iconData.alt or '' ) end -- Implements [[Template:Icon link]], a superset of [[Template:Icon]] -- Returns an icon, plus a suitably formatted wikilink function p._link(args, data) args.size = args.size or args.iconsize local icon = p._main(args, data) -- If no link given in args[2], default back to [[Template:Icon]] if not args[2] then return icon end -- Strip wiki markup out of link getPlain = getPlain or require("Module:Text").Text().getPlain local link = getPlain(args[2]) local display = args[3] or args[2] -- italicize display string, if requested if yesNo(args.i) or yesNo(args.italic) or yesNo(args.italics) then display = '<i>'..display..'</i>' end -- if display is link, just use standard wlink if link == display then return icon..'&nbsp;[['..link..']]' end return icon..'&nbsp;[['..link..'|'..display..']]' end function p.main(frame) local args = getArgs(frame,{parentFirst=true}) return p._main(args) end function p.link(frame) local args = getArgs(frame,{parentFirst=true}) return p._link(args) end return p 7688d9a465bd7c4caa51f7e5c02676c162d583f5 Module:Icon/data 828 438 877 876 2023-07-10T15:58:08Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Icon/data]] Scribunto text/plain -- This module stores icon data for [[Module:Icon]]. -------------------------------------------------------------------------------- -- Icon data -------------------------------------------------------------------------------- local data = { fa = { image = "Featured article star.svg", tooltip = "Featured article", link = true, }, far = { image = "Cscr-star piece.png", tooltip = "Featured article review", link = true, }, farc = { image = "Cscr-star piece.png", tooltip = "Featured article removal candidate", link = true, }, ffa = { aliases = {"dfa"}, image = "Featured article star - cross.svg", tooltip = "Former featured article", link = true, }, fac = { aliases = {"fan"}, image = "Cscr-candidate.svg", tooltip = "Featured article candidate", link = true, }, ffac = { aliases = {"nofa"}, image = "Featured article star - cross.svg", tooltip = "Failed featured article candidate", link = true, }, fl = { image = "Featured article star.svg", tooltip = "Featured list", link = true, }, flrc = { aliases = {"flr"}, image = "Cscr-star piece.png", tooltip = "Featured list removal candidate", link = true, }, ffl = { aliases = {"dfl"}, image = "Cscr-featured-strike.svg", tooltip = "Former featured list", link = true, }, flc = { aliases = {"fln"}, image = "Cscr-candidate.svg", tooltip = "Featured list candidate", link = true, }, fflc = { aliases = {"nofl"}, image = "Cscr-former.svg", tooltip = "Failed featured list candidate", link = true, }, a = { image = "Symbol a class.svg", tooltip = "A-Class article", link = true, }, dac = { aliases = {"daa"}, image = "Symbol unsupport A vote.svg", tooltip = "Demoted A-Class article", link = true, }, acc = { aliases = {"acn", "aac"}, image = "A candidate.svg", tooltip = "A-Class article candidate", link = true, }, noac = { aliases = {"faac"}, image = "Symbol unsupport A vote.svg", tooltip = "Failed A-Class article candidate", link = true, }, ga = { image = "Symbol support vote.svg", tooltip = "Good article", link = false, }, gar = { image = "GA Candidate Neutral vote(ChaosNil).svg", tooltip = "Good article reassessment", link = false, }, dga = { image = "Symbol unsupport vote.svg", tooltip = "Delisted good article", link = false, }, gan = { aliases = {"gac"}, image = "GA candidate.svg", tooltip = "Good article nominee", link = false, }, ga2 = { image = "Symbol neutral vote.svg", tooltip = "Good article, 2nd opinion", link = false, }, gah = { image = "Symbol wait.svg", tooltip = "Good article on hold", link = false, }, fgan = { aliases = {"noga", "gaf", "gf"}, image = "Symbol oppose vote.svg", tooltip = "Failed good article nominee", link = false, }, fp = { image = "Cscr-featured.svg", tooltip = "Featured picture", link = true, }, fpc = { aliases = {"fpn"}, image = "Cscr-candidate.svg", tooltip = "Featured picture candidate", link = true, }, ffp = { image = "Cscr-former.svg", tooltip = "Former featured picture", link = true, }, vp = { image = "ENWP VP Logo.svg", tooltip = "Valued picture", link = true, }, vpc = { image = "Valued pics 1.svg", tooltip = "Valued picture candidate", link = true, }, fs = { image = "Cscr-featured.svg", tooltip = "Featured sound", link = true, }, ffs = { image = "Cscr-former.svg", tooltip = "Former featured sound", link = true, }, fsc = { image = "Cscr-candidate.svg", tooltip = "Featured sound candidate", link = true, }, fpo = { image = "Linecons big-star.svg", tooltip = "Before the featured portal process ceased in 2017, this had been designated as a featured portal.", link = true, }, fpor = { image = "Cscr-star piece.png", tooltip = "Featured portal review", link = true, }, ffpo = { image = "Featured article star - cross.svg", tooltip = "Former featured portal", link = true, }, fpoc = { image = "Cscr-candidate.svg", tooltip = "Featured portal candidate", link = true, }, ft = { image = "Cscr-featuredtopic.svg", tooltip = "Featured topic", link = true, }, ftrc = { image = "Cscr-star piece.png", tooltip = "Featured topic removal candidate", link = true, }, fft = { aliases = {"dft"}, image = "DFT candidate_cluster.svg", tooltip = "Former featured topic", link = true, }, ftc = { aliases = {"ftn"}, image = "FT candidate cluster.svg", tooltip = "Featured topic candidate", link = false, }, gt = { image = "Support cluster.svg", tooltip = "Good topic", link = false, }, gtrc = { image = "Symbol unsupport vote.svg", tooltip = "Good topic removal candidate", link = false, }, gtc = { aliases = {"gtn"}, image = "GA candidate cluster.svg", tooltip = "Good topic candidate", link = false, }, bplus = { aliases = {"b+"}, image = "Symbol bplus class.svg", tooltip = "Bplus-Class article", link = true, }, b = { image = "Symbol b class.svg", tooltip = "B-Class article", link = true, }, br = { aliases = {"bcr"}, image = "Bclass-checklist.svg", tooltip = "B-Class review", link = true, }, c = { image = "Symbol c class.svg", tooltip = "C-Class article", link = true, }, start = { image = "Symbol start class.svg", tooltip = "Start-Class article", link = true, }, stub = { image = "Symbol stub class.svg", tooltip = "Stub-Class article", link = true, }, list = { aliases = {"comparison"}, image = "Symbol list class.svg", tooltip = "List-Class article", link = false, }, no = { image = "Crystal button cancel.svg", tooltip = "Unknown-Class article", link = true, }, book = { image = "Symbol book class2.svg", tooltip = "Wikipedia book", link = true, }, category = { aliases = {"cat", "categ"}, image = "Symbol category class.svg", tooltip = "Category", link = false, }, disambiguation = { aliases = {"dab", "disamb", "disambig"}, image = "Symbol dab class.svg", tooltip = "Disambiguation page", link = true, }, image = { aliases = {"file"}, image = "Symbol file class.svg", tooltip = "File", link = true, }, needed = { image = "Symbol needed class.svg", tooltip = "Needed article", link = false, }, outline = { image = "Global thinking.svg", tooltip = "Outline", link = false, }, portal = { image = "Symbol portal class.svg", tooltip = "Portal", link = true, }, project = { image = "Symbol project class.svg", tooltip = "Project page", link = false, }, redirect = { aliases = {"red", "redir"}, image = "Symbol redirect vote2.svg", tooltip = "Redirect", link = true, }, template = { aliases = {"temp", "templ"}, image = "Symbol template class.svg", tooltip = "Template", link = false, }, essay = { image = "Essay.svg", tooltip = "Essay", link = false, }, na = { image = "Symbol na class.svg", tooltip = "Non-article page", link = true, }, aa = { image = "Yes check.svg", tooltip = "Audited article of limited subject matter", link = false, }, da = { image = "Symbol oppose vote.svg", tooltip = "Demoted article", link = false, }, dyk = { image = "Symbol question.svg", tooltip = "Did You Know?", link = false, }, dyk2 = { image = "DYK questionmark icon.svg", tooltip = "Did You Know?", link = false, }, pr = { image = "Nuvola apps kedit.png", tooltip = "Peer review", link = true, }, ppr = { image = "Nuvola apps kedit.png", tooltip = "Portal peer review", link = true, }, q = { aliases = {"question"}, image = "Symbol question.svg", tooltip = "Question", link = false, }, cleanup = { image = "Edit-clear.svg", tooltip = "Cleanup work", link = false, }, qi = { image = "Quality images logo.svg", tooltip = "Quality image on Wikimedia Commons", link = false, }, vi = { image = "Valued image seal.svg", tooltip = "Valued image on Wikimedia Commons", link = false, }, tfa = { image = "Wikipedia-logo.svg", tooltip = "Today's Featured Article", link = true, }, tfl = { image = "Wikipedia-logo.svg", tooltip = "Today's Featured List", link = true, }, itn = { image = "Globe current.svg", tooltip = "In The News", link = true, }, otd = { image = "Nuvola apps date.svg", tooltip = "On This Day", link = true, }, wikiproject = { image = "People icon.svg", tooltip = "WikiProject", link = false, }, goce = { image = "Writing Magnifying.PNG", tooltip = "Guild of Copy Editors", link = true, }, wikipedia = { image = "Wikipedia-logo.svg", tooltip = "Wikipedia page", link = true, }, commons = { image = "Commons-logo.svg", tooltip = "Commons page", link = false, }, wikiquote = { image = "Wikiquote-logo.svg", tooltip = "Wikiquote page", link = false, }, wikiversity = { image = "Wikiversity logo 2017.svg", tooltip = "Wikiversity page", link = true, }, wikibooks = { image = "Wikibooks-logo.svg", tooltip = "Wikibooks page", link = true, }, wikisource = { image = "Wikisource-logo.svg", tooltip = "Wikisource page", link = true, }, wiktionary = { image = "Wiktionary-logo.svg", tooltip = "Wiktionary page", link = true, }, wikinews = { image = "Wikinews-logo.svg", tooltip = "Wikinews page", link = true, }, wikispecies = { image = "Wikispecies-logo.svg", tooltip = "Wikispecies page", link = true, }, wikidata = { image = "Wikidata-logo.svg", tooltip = "Wikidata page", link = false, }, wikivoyage = { image = "Wikivoyage-logo.svg", tooltip = "Wikivoyage page", link = true, }, mediawiki = { image = "MediaWiki-2020-icon.svg", tooltip = "MediaWiki", link = false, }, phabricator = { aliases = {"phab"}, image = "Favicon-Phabricator-WM.svg", tooltip = "Phabricator", link = false, }, wikitech = { image = "Wikitech-2021-blue-icon.svg", tooltip = "Wikitech", link = false, }, meta = { image = "Wikimedia Community Logo.svg", tooltip = "Meta-wiki page", link = false, }, four = { aliases = {"4a"}, image = "Four Award.svg", tooltip = "Four Award", link = false, }, million = { image = "Million award logo.svg", tooltip = "Million Award", link = true, }, module = { image = "Lua-logo-nolabel.svg", tooltip = "Module", link = false, }, vital = { image = "Círculos_Concéntricos.svg", tooltip = "Vital article", link = false, }, potd = { image = "Wikipedia-logo.svg", tooltip = "Picture of the Day", link = true, }, _DEFAULT = { image = "Symbol question.svg", link = false, } } -------------------------------------------------------------------------------- -- End icon data -------------------------------------------------------------------------------- -- Make aliases work the same as normal keys, and remove the "aliases" subtables. local ret= {} for code, iconData in pairs(data) do iconData.canonicalCode = code if iconData.aliases then for _, alias in ipairs(iconData.aliases) do ret[alias] = iconData end iconData.aliases = nil end ret[code] = iconData end return ret d8e668d4755103abdbc8325fa35964e99198c29d Module:Date 828 439 879 878 2023-07-10T15:58:08Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Date]] Scribunto text/plain -- Date functions for use by other modules. -- I18N and time zones are not supported. local MINUS = '−' -- Unicode U+2212 MINUS SIGN local floor = math.floor local Date, DateDiff, diffmt -- forward declarations local uniq = { 'unique identifier' } local function is_date(t) -- The system used to make a date read-only means there is no unique -- metatable that is conveniently accessible to check. return type(t) == 'table' and t._id == uniq end local function is_diff(t) return type(t) == 'table' and getmetatable(t) == diffmt end local function _list_join(list, sep) return table.concat(list, sep) end local function collection() -- Return a table to hold items. return { n = 0, add = function (self, item) self.n = self.n + 1 self[self.n] = item end, join = _list_join, } end local function strip_to_nil(text) -- If text is a string, return its trimmed content, or nil if empty. -- Otherwise return text (convenient when Date fields are provided from -- another module which may pass a string, a number, or another type). if type(text) == 'string' then text = text:match('(%S.-)%s*$') end return text end local function is_leap_year(year, calname) -- Return true if year is a leap year. if calname == 'Julian' then return year % 4 == 0 end return (year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0 end local function days_in_month(year, month, calname) -- Return number of days (1..31) in given month (1..12). if month == 2 and is_leap_year(year, calname) then return 29 end return ({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })[month] end local function h_m_s(time) -- Return hour, minute, second extracted from fraction of a day. time = floor(time * 24 * 3600 + 0.5) -- number of seconds local second = time % 60 time = floor(time / 60) return floor(time / 60), time % 60, second end local function hms(date) -- Return fraction of a day from date's time, where (0 <= fraction < 1) -- if the values are valid, but could be anything if outside range. return (date.hour + (date.minute + date.second / 60) / 60) / 24 end local function julian_date(date) -- Return jd, jdz from a Julian or Gregorian calendar date where -- jd = Julian date and its fractional part is zero at noon -- jdz = same, but assume time is 00:00:00 if no time given -- http://www.tondering.dk/claus/cal/julperiod.php#formula -- Testing shows this works for all dates from year -9999 to 9999! -- JDN 0 is the 24-hour period starting at noon UTC on Monday -- 1 January 4713 BC = (-4712, 1, 1) Julian calendar -- 24 November 4714 BC = (-4713, 11, 24) Gregorian calendar local offset local a = floor((14 - date.month)/12) local y = date.year + 4800 - a if date.calendar == 'Julian' then offset = floor(y/4) - 32083 else offset = floor(y/4) - floor(y/100) + floor(y/400) - 32045 end local m = date.month + 12*a - 3 local jd = date.day + floor((153*m + 2)/5) + 365*y + offset if date.hastime then jd = jd + hms(date) - 0.5 return jd, jd end return jd, jd - 0.5 end local function set_date_from_jd(date) -- Set the fields of table date from its Julian date field. -- Return true if date is valid. -- http://www.tondering.dk/claus/cal/julperiod.php#formula -- This handles the proleptic Julian and Gregorian calendars. -- Negative Julian dates are not defined but they work. local calname = date.calendar local low, high -- min/max limits for date ranges −9999-01-01 to 9999-12-31 if calname == 'Gregorian' then low, high = -1930999.5, 5373484.49999 elseif calname == 'Julian' then low, high = -1931076.5, 5373557.49999 else return end local jd = date.jd if not (type(jd) == 'number' and low <= jd and jd <= high) then return end local jdn = floor(jd) if date.hastime then local time = jd - jdn -- 0 <= time < 1 if time >= 0.5 then -- if at or after midnight of next day jdn = jdn + 1 time = time - 0.5 else time = time + 0.5 end date.hour, date.minute, date.second = h_m_s(time) else date.second = 0 date.minute = 0 date.hour = 0 end local b, c if calname == 'Julian' then b = 0 c = jdn + 32082 else -- Gregorian local a = jdn + 32044 b = floor((4*a + 3)/146097) c = a - floor(146097*b/4) end local d = floor((4*c + 3)/1461) local e = c - floor(1461*d/4) local m = floor((5*e + 2)/153) date.day = e - floor((153*m + 2)/5) + 1 date.month = m + 3 - 12*floor(m/10) date.year = 100*b + d - 4800 + floor(m/10) return true end local function fix_numbers(numbers, y, m, d, H, M, S, partial, hastime, calendar) -- Put the result of normalizing the given values in table numbers. -- The result will have valid m, d values if y is valid; caller checks y. -- The logic of PHP mktime is followed where m or d can be zero to mean -- the previous unit, and -1 is the one before that, etc. -- Positive values carry forward. local date if not (1 <= m and m <= 12) then date = Date(y, 1, 1) if not date then return end date = date + ((m - 1) .. 'm') y, m = date.year, date.month end local days_hms if not partial then if hastime and H and M and S then if not (0 <= H and H <= 23 and 0 <= M and M <= 59 and 0 <= S and S <= 59) then days_hms = hms({ hour = H, minute = M, second = S }) end end if days_hms or not (1 <= d and d <= days_in_month(y, m, calendar)) then date = date or Date(y, m, 1) if not date then return end date = date + (d - 1 + (days_hms or 0)) y, m, d = date.year, date.month, date.day if days_hms then H, M, S = date.hour, date.minute, date.second end end end numbers.year = y numbers.month = m numbers.day = d if days_hms then -- Don't set H unless it was valid because a valid H will set hastime. numbers.hour = H numbers.minute = M numbers.second = S end end local function set_date_from_numbers(date, numbers, options) -- Set the fields of table date from numeric values. -- Return true if date is valid. if type(numbers) ~= 'table' then return end local y = numbers.year or date.year local m = numbers.month or date.month local d = numbers.day or date.day local H = numbers.hour local M = numbers.minute or date.minute or 0 local S = numbers.second or date.second or 0 local need_fix if y and m and d then date.partial = nil if not (-9999 <= y and y <= 9999 and 1 <= m and m <= 12 and 1 <= d and d <= days_in_month(y, m, date.calendar)) then if not date.want_fix then return end need_fix = true end elseif y and date.partial then if d or not (-9999 <= y and y <= 9999) then return end if m and not (1 <= m and m <= 12) then if not date.want_fix then return end need_fix = true end else return end if date.partial then H = nil -- ignore any time M = nil S = nil else if H then -- It is not possible to set M or S without also setting H. date.hastime = true else H = 0 end if not (0 <= H and H <= 23 and 0 <= M and M <= 59 and 0 <= S and S <= 59) then if date.want_fix then need_fix = true else return end end end date.want_fix = nil if need_fix then fix_numbers(numbers, y, m, d, H, M, S, date.partial, date.hastime, date.calendar) return set_date_from_numbers(date, numbers, options) end date.year = y -- -9999 to 9999 ('n BC' → year = 1 - n) date.month = m -- 1 to 12 (may be nil if partial) date.day = d -- 1 to 31 (* = nil if partial) date.hour = H -- 0 to 59 (*) date.minute = M -- 0 to 59 (*) date.second = S -- 0 to 59 (*) if type(options) == 'table' then for _, k in ipairs({ 'am', 'era', 'format' }) do if options[k] then date.options[k] = options[k] end end end return true end local function make_option_table(options1, options2) -- If options1 is a string, return a table with its settings, or -- if it is a table, use its settings. -- Missing options are set from table options2 or defaults. -- If a default is used, a flag is set so caller knows the value was not intentionally set. -- Valid option settings are: -- am: 'am', 'a.m.', 'AM', 'A.M.' -- 'pm', 'p.m.', 'PM', 'P.M.' (each has same meaning as corresponding item above) -- era: 'BCMINUS', 'BCNEGATIVE', 'BC', 'B.C.', 'BCE', 'B.C.E.', 'AD', 'A.D.', 'CE', 'C.E.' -- Option am = 'am' does not mean the hour is AM; it means 'am' or 'pm' is used, depending on the hour, -- and am = 'pm' has the same meaning. -- Similarly, era = 'BC' means 'BC' is used if year <= 0. -- BCMINUS displays a MINUS if year < 0 and the display format does not include %{era}. -- BCNEGATIVE is similar but displays a hyphen. local result = { bydefault = {} } if type(options1) == 'table' then result.am = options1.am result.era = options1.era elseif type(options1) == 'string' then -- Example: 'am:AM era:BC' or 'am=AM era=BC'. for item in options1:gmatch('%S+') do local lhs, rhs = item:match('^(%w+)[:=](.+)$') if lhs then result[lhs] = rhs end end end options2 = type(options2) == 'table' and options2 or {} local defaults = { am = 'am', era = 'BC' } for k, v in pairs(defaults) do if not result[k] then if options2[k] then result[k] = options2[k] else result[k] = v result.bydefault[k] = true end end end return result end local ampm_options = { -- lhs = input text accepted as an am/pm option -- rhs = code used internally ['am'] = 'am', ['AM'] = 'AM', ['a.m.'] = 'a.m.', ['A.M.'] = 'A.M.', ['pm'] = 'am', -- same as am ['PM'] = 'AM', ['p.m.'] = 'a.m.', ['P.M.'] = 'A.M.', } local era_text = { -- Text for displaying an era with a positive year (after adjusting -- by replacing year with 1 - year if date.year <= 0). -- options.era = { year<=0 , year>0 } ['BCMINUS'] = { 'BC' , '' , isbc = true, sign = MINUS }, ['BCNEGATIVE'] = { 'BC' , '' , isbc = true, sign = '-' }, ['BC'] = { 'BC' , '' , isbc = true }, ['B.C.'] = { 'B.C.' , '' , isbc = true }, ['BCE'] = { 'BCE' , '' , isbc = true }, ['B.C.E.'] = { 'B.C.E.', '' , isbc = true }, ['AD'] = { 'BC' , 'AD' }, ['A.D.'] = { 'B.C.' , 'A.D.' }, ['CE'] = { 'BCE' , 'CE' }, ['C.E.'] = { 'B.C.E.', 'C.E.' }, } local function get_era_for_year(era, year) return (era_text[era] or era_text['BC'])[year > 0 and 2 or 1] or '' end local function strftime(date, format, options) -- Return date formatted as a string using codes similar to those -- in the C strftime library function. local sformat = string.format local shortcuts = { ['%c'] = '%-I:%M %p %-d %B %-Y %{era}', -- date and time: 2:30 pm 1 April 2016 ['%x'] = '%-d %B %-Y %{era}', -- date: 1 April 2016 ['%X'] = '%-I:%M %p', -- time: 2:30 pm } if shortcuts[format] then format = shortcuts[format] end local codes = { a = { field = 'dayabbr' }, A = { field = 'dayname' }, b = { field = 'monthabbr' }, B = { field = 'monthname' }, u = { fmt = '%d' , field = 'dowiso' }, w = { fmt = '%d' , field = 'dow' }, d = { fmt = '%02d', fmt2 = '%d', field = 'day' }, m = { fmt = '%02d', fmt2 = '%d', field = 'month' }, Y = { fmt = '%04d', fmt2 = '%d', field = 'year' }, H = { fmt = '%02d', fmt2 = '%d', field = 'hour' }, M = { fmt = '%02d', fmt2 = '%d', field = 'minute' }, S = { fmt = '%02d', fmt2 = '%d', field = 'second' }, j = { fmt = '%03d', fmt2 = '%d', field = 'dayofyear' }, I = { fmt = '%02d', fmt2 = '%d', field = 'hour', special = 'hour12' }, p = { field = 'hour', special = 'am' }, } options = make_option_table(options, date.options) local amopt = options.am local eraopt = options.era local function replace_code(spaces, modifier, id) local code = codes[id] if code then local fmt = code.fmt if modifier == '-' and code.fmt2 then fmt = code.fmt2 end local value = date[code.field] if not value then return nil -- an undefined field in a partial date end local special = code.special if special then if special == 'hour12' then value = value % 12 value = value == 0 and 12 or value elseif special == 'am' then local ap = ({ ['a.m.'] = { 'a.m.', 'p.m.' }, ['AM'] = { 'AM', 'PM' }, ['A.M.'] = { 'A.M.', 'P.M.' }, })[ampm_options[amopt]] or { 'am', 'pm' } return (spaces == '' and '' or '&nbsp;') .. (value < 12 and ap[1] or ap[2]) end end if code.field == 'year' then local sign = (era_text[eraopt] or {}).sign if not sign or format:find('%{era}', 1, true) then sign = '' if value <= 0 then value = 1 - value end else if value >= 0 then sign = '' else value = -value end end return spaces .. sign .. sformat(fmt, value) end return spaces .. (fmt and sformat(fmt, value) or value) end end local function replace_property(spaces, id) if id == 'era' then -- Special case so can use local era option. local result = get_era_for_year(eraopt, date.year) if result == '' then return '' end return (spaces == '' and '' or '&nbsp;') .. result end local result = date[id] if type(result) == 'string' then return spaces .. result end if type(result) == 'number' then return spaces .. tostring(result) end if type(result) == 'boolean' then return spaces .. (result and '1' or '0') end -- This occurs if id is an undefined field in a partial date, or is the name of a function. return nil end local PERCENT = '\127PERCENT\127' return (format :gsub('%%%%', PERCENT) :gsub('(%s*)%%{(%w+)}', replace_property) :gsub('(%s*)%%(%-?)(%a)', replace_code) :gsub(PERCENT, '%%') ) end local function _date_text(date, fmt, options) -- Return a formatted string representing the given date. if not is_date(date) then error('date:text: need a date (use "date:text()" with a colon)', 2) end if type(fmt) == 'string' and fmt:match('%S') then if fmt:find('%', 1, true) then return strftime(date, fmt, options) end elseif date.partial then fmt = date.month and 'my' or 'y' else fmt = 'dmy' if date.hastime then fmt = (date.second > 0 and 'hms ' or 'hm ') .. fmt end end local function bad_format() -- For consistency with other format processing, return given format -- (or cleaned format if original was not a string) if invalid. return mw.text.nowiki(fmt) end if date.partial then -- Ignore days in standard formats like 'ymd'. if fmt == 'ym' or fmt == 'ymd' then fmt = date.month and '%Y-%m %{era}' or '%Y %{era}' elseif fmt == 'my' or fmt == 'dmy' or fmt == 'mdy' then fmt = date.month and '%B %-Y %{era}' or '%-Y %{era}' elseif fmt == 'y' then fmt = date.month and '%-Y %{era}' or '%-Y %{era}' else return bad_format() end return strftime(date, fmt, options) end local function hm_fmt() local plain = make_option_table(options, date.options).bydefault.am return plain and '%H:%M' or '%-I:%M %p' end local need_time = date.hastime local t = collection() for item in fmt:gmatch('%S+') do local f if item == 'hm' then f = hm_fmt() need_time = false elseif item == 'hms' then f = '%H:%M:%S' need_time = false elseif item == 'ymd' then f = '%Y-%m-%d %{era}' elseif item == 'mdy' then f = '%B %-d, %-Y %{era}' elseif item == 'dmy' then f = '%-d %B %-Y %{era}' else return bad_format() end t:add(f) end fmt = t:join(' ') if need_time then fmt = hm_fmt() .. ' ' .. fmt end return strftime(date, fmt, options) end local day_info = { -- 0=Sun to 6=Sat [0] = { 'Sun', 'Sunday' }, { 'Mon', 'Monday' }, { 'Tue', 'Tuesday' }, { 'Wed', 'Wednesday' }, { 'Thu', 'Thursday' }, { 'Fri', 'Friday' }, { 'Sat', 'Saturday' }, } local month_info = { -- 1=Jan to 12=Dec { 'Jan', 'January' }, { 'Feb', 'February' }, { 'Mar', 'March' }, { 'Apr', 'April' }, { 'May', 'May' }, { 'Jun', 'June' }, { 'Jul', 'July' }, { 'Aug', 'August' }, { 'Sep', 'September' }, { 'Oct', 'October' }, { 'Nov', 'November' }, { 'Dec', 'December' }, } local function name_to_number(text, translate) if type(text) == 'string' then return translate[text:lower()] end end local function day_number(text) return name_to_number(text, { sun = 0, sunday = 0, mon = 1, monday = 1, tue = 2, tuesday = 2, wed = 3, wednesday = 3, thu = 4, thursday = 4, fri = 5, friday = 5, sat = 6, saturday = 6, }) end local function month_number(text) return name_to_number(text, { jan = 1, january = 1, feb = 2, february = 2, mar = 3, march = 3, apr = 4, april = 4, may = 5, jun = 6, june = 6, jul = 7, july = 7, aug = 8, august = 8, sep = 9, september = 9, sept = 9, oct = 10, october = 10, nov = 11, november = 11, dec = 12, december = 12, }) end local function _list_text(list, fmt) -- Return a list of formatted strings from a list of dates. if not type(list) == 'table' then error('date:list:text: need "list:text()" with a colon', 2) end local result = { join = _list_join } for i, date in ipairs(list) do result[i] = date:text(fmt) end return result end local function _date_list(date, spec) -- Return a possibly empty numbered table of dates meeting the specification. -- Dates in the list are in ascending order (oldest date first). -- The spec should be a string of form "<count> <day> <op>" -- where each item is optional and -- count = number of items wanted in list -- day = abbreviation or name such as Mon or Monday -- op = >, >=, <, <= (default is > meaning after date) -- If no count is given, the list is for the specified days in date's month. -- The default day is date's day. -- The spec can also be a positive or negative number: -- -5 is equivalent to '5 <' -- 5 is equivalent to '5' which is '5 >' if not is_date(date) then error('date:list: need a date (use "date:list()" with a colon)', 2) end local list = { text = _list_text } if date.partial then return list end local count, offset, operation local ops = { ['>='] = { before = false, include = true }, ['>'] = { before = false, include = false }, ['<='] = { before = true , include = true }, ['<'] = { before = true , include = false }, } if spec then if type(spec) == 'number' then count = floor(spec + 0.5) if count < 0 then count = -count operation = ops['<'] end elseif type(spec) == 'string' then local num, day, op = spec:match('^%s*(%d*)%s*(%a*)%s*([<>=]*)%s*$') if not num then return list end if num ~= '' then count = tonumber(num) end if day ~= '' then local dow = day_number(day:gsub('[sS]$', '')) -- accept plural days if not dow then return list end offset = dow - date.dow end operation = ops[op] else return list end end offset = offset or 0 operation = operation or ops['>'] local datefrom, dayfirst, daylast if operation.before then if offset > 0 or (offset == 0 and not operation.include) then offset = offset - 7 end if count then if count > 1 then offset = offset - 7*(count - 1) end datefrom = date + offset else daylast = date.day + offset dayfirst = daylast % 7 if dayfirst == 0 then dayfirst = 7 end end else if offset < 0 or (offset == 0 and not operation.include) then offset = offset + 7 end if count then datefrom = date + offset else dayfirst = date.day + offset daylast = date.monthdays end end if not count then if daylast < dayfirst then return list end count = floor((daylast - dayfirst)/7) + 1 datefrom = Date(date, {day = dayfirst}) end for i = 1, count do if not datefrom then break end -- exceeds date limits list[i] = datefrom datefrom = datefrom + 7 end return list end -- A table to get the current date/time (UTC), but only if needed. local current = setmetatable({}, { __index = function (self, key) local d = os.date('!*t') self.year = d.year self.month = d.month self.day = d.day self.hour = d.hour self.minute = d.min self.second = d.sec return rawget(self, key) end }) local function extract_date(newdate, text) -- Parse the date/time in text and return n, o where -- n = table of numbers with date/time fields -- o = table of options for AM/PM or AD/BC or format, if any -- or return nothing if date is known to be invalid. -- Caller determines if the values in n are valid. -- A year must be positive ('1' to '9999'); use 'BC' for BC. -- In a y-m-d string, the year must be four digits to avoid ambiguity -- ('0001' to '9999'). The only way to enter year <= 0 is by specifying -- the date as three numeric parameters like ymd Date(-1, 1, 1). -- Dates of form d/m/y, m/d/y, y/m/d are rejected as potentially ambiguous. local date, options = {}, {} if text:sub(-1) == 'Z' then -- Extract date/time from a Wikidata timestamp. -- The year can be 1 to 16 digits but this module handles 1 to 4 digits only. -- Examples: '+2016-06-21T14:30:00Z', '-0000000180-00-00T00:00:00Z'. local sign, y, m, d, H, M, S = text:match('^([+%-])(%d+)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)Z$') if sign then y = tonumber(y) if sign == '-' and y > 0 then y = -y end if y <= 0 then options.era = 'BCE' end date.year = y m = tonumber(m) d = tonumber(d) H = tonumber(H) M = tonumber(M) S = tonumber(S) if m == 0 then newdate.partial = true return date, options end date.month = m if d == 0 then newdate.partial = true return date, options end date.day = d if H > 0 or M > 0 or S > 0 then date.hour = H date.minute = M date.second = S end return date, options end return end local function extract_ymd(item) -- Called when no day or month has been set. local y, m, d = item:match('^(%d%d%d%d)%-(%w+)%-(%d%d?)$') if y then if date.year then return end if m:match('^%d%d?$') then m = tonumber(m) else m = month_number(m) end if m then date.year = tonumber(y) date.month = m date.day = tonumber(d) return true end end end local function extract_day_or_year(item) -- Called when a day would be valid, or -- when a year would be valid if no year has been set and partial is set. local number, suffix = item:match('^(%d%d?%d?%d?)(.*)$') if number then local n = tonumber(number) if #number <= 2 and n <= 31 then suffix = suffix:lower() if suffix == '' or suffix == 'st' or suffix == 'nd' or suffix == 'rd' or suffix == 'th' then date.day = n return true end elseif suffix == '' and newdate.partial and not date.year then date.year = n return true end end end local function extract_month(item) -- A month must be given as a name or abbreviation; a number could be ambiguous. local m = month_number(item) if m then date.month = m return true end end local function extract_time(item) local h, m, s = item:match('^(%d%d?):(%d%d)(:?%d*)$') if date.hour or not h then return end if s ~= '' then s = s:match('^:(%d%d)$') if not s then return end end date.hour = tonumber(h) date.minute = tonumber(m) date.second = tonumber(s) -- nil if empty string return true end local item_count = 0 local index_time local function set_ampm(item) local H = date.hour if H and not options.am and index_time + 1 == item_count then options.am = ampm_options[item] -- caller checked this is not nil if item:match('^[Aa]') then if not (1 <= H and H <= 12) then return end if H == 12 then date.hour = 0 end else if not (1 <= H and H <= 23) then return end if H <= 11 then date.hour = H + 12 end end return true end end for item in text:gsub(',', ' '):gsub('&nbsp;', ' '):gmatch('%S+') do item_count = item_count + 1 if era_text[item] then -- Era is accepted in peculiar places. if options.era then return end options.era = item elseif ampm_options[item] then if not set_ampm(item) then return end elseif item:find(':', 1, true) then if not extract_time(item) then return end index_time = item_count elseif date.day and date.month then if date.year then return -- should be nothing more so item is invalid end if not item:match('^(%d%d?%d?%d?)$') then return end date.year = tonumber(item) elseif date.day then if not extract_month(item) then return end elseif date.month then if not extract_day_or_year(item) then return end elseif extract_month(item) then options.format = 'mdy' elseif extract_ymd(item) then options.format = 'ymd' elseif extract_day_or_year(item) then if date.day then options.format = 'dmy' end else return end end if not date.year or date.year == 0 then return end local era = era_text[options.era] if era and era.isbc then date.year = 1 - date.year end return date, options end local function autofill(date1, date2) -- Fill any missing month or day in each date using the -- corresponding component from the other date, if present, -- or with 1 if both dates are missing the month or day. -- This gives a good result for calculating the difference -- between two partial dates when no range is wanted. -- Return filled date1, date2 (two full dates). local function filled(a, b) -- Return date a filled, if necessary, with month and/or day from date b. -- The filled day is truncated to fit the number of days in the month. local fillmonth, fillday if not a.month then fillmonth = b.month or 1 end if not a.day then fillday = b.day or 1 end if fillmonth or fillday then -- need to create a new date a = Date(a, { month = fillmonth, day = math.min(fillday or a.day, days_in_month(a.year, fillmonth or a.month, a.calendar)) }) end return a end return filled(date1, date2), filled(date2, date1) end local function date_add_sub(lhs, rhs, is_sub) -- Return a new date from calculating (lhs + rhs) or (lhs - rhs), -- or return nothing if invalid. -- The result is nil if the calculated date exceeds allowable limits. -- Caller ensures that lhs is a date; its properties are copied for the new date. if lhs.partial then -- Adding to a partial is not supported. -- Can subtract a date or partial from a partial, but this is not called for that. return end local function is_prefix(text, word, minlen) local n = #text return (minlen or 1) <= n and n <= #word and text == word:sub(1, n) end local function do_days(n) local forcetime, jd if floor(n) == n then jd = lhs.jd else forcetime = not lhs.hastime jd = lhs.jdz end jd = jd + (is_sub and -n or n) if forcetime then jd = tostring(jd) if not jd:find('.', 1, true) then jd = jd .. '.0' end end return Date(lhs, 'juliandate', jd) end if type(rhs) == 'number' then -- Add/subtract days, including fractional days. return do_days(rhs) end if type(rhs) == 'string' then -- rhs is a single component like '26m' or '26 months' (with optional sign). -- Fractions like '3.25d' are accepted for the units which are handled as days. local sign, numstr, id = rhs:match('^%s*([+-]?)([%d%.]+)%s*(%a+)$') if sign then if sign == '-' then is_sub = not (is_sub and true or false) end local y, m, days local num = tonumber(numstr) if not num then return end id = id:lower() if is_prefix(id, 'years') then y = num m = 0 elseif is_prefix(id, 'months') then y = floor(num / 12) m = num % 12 elseif is_prefix(id, 'weeks') then days = num * 7 elseif is_prefix(id, 'days') then days = num elseif is_prefix(id, 'hours') then days = num / 24 elseif is_prefix(id, 'minutes', 3) then days = num / (24 * 60) elseif is_prefix(id, 'seconds') then days = num / (24 * 3600) else return end if days then return do_days(days) end if numstr:find('.', 1, true) then return end if is_sub then y = -y m = -m end assert(-11 <= m and m <= 11) y = lhs.year + y m = lhs.month + m if m > 12 then y = y + 1 m = m - 12 elseif m < 1 then y = y - 1 m = m + 12 end local d = math.min(lhs.day, days_in_month(y, m, lhs.calendar)) return Date(lhs, y, m, d) end end if is_diff(rhs) then local days = rhs.age_days if (is_sub or false) ~= (rhs.isnegative or false) then days = -days end return lhs + days end end local full_date_only = { dayabbr = true, dayname = true, dow = true, dayofweek = true, dowiso = true, dayofweekiso = true, dayofyear = true, gsd = true, juliandate = true, jd = true, jdz = true, jdnoon = true, } -- Metatable for a date's calculated fields. local datemt = { __index = function (self, key) if rawget(self, 'partial') then if full_date_only[key] then return end if key == 'monthabbr' or key == 'monthdays' or key == 'monthname' then if not self.month then return end end end local value if key == 'dayabbr' then value = day_info[self.dow][1] elseif key == 'dayname' then value = day_info[self.dow][2] elseif key == 'dow' then value = (self.jdnoon + 1) % 7 -- day-of-week 0=Sun to 6=Sat elseif key == 'dayofweek' then value = self.dow elseif key == 'dowiso' then value = (self.jdnoon % 7) + 1 -- ISO day-of-week 1=Mon to 7=Sun elseif key == 'dayofweekiso' then value = self.dowiso elseif key == 'dayofyear' then local first = Date(self.year, 1, 1, self.calendar).jdnoon value = self.jdnoon - first + 1 -- day-of-year 1 to 366 elseif key == 'era' then -- Era text (never a negative sign) from year and options. value = get_era_for_year(self.options.era, self.year) elseif key == 'format' then value = self.options.format or 'dmy' elseif key == 'gsd' then -- GSD = 1 from 00:00:00 to 23:59:59 on 1 January 1 AD Gregorian calendar, -- which is from jd 1721425.5 to 1721426.49999. value = floor(self.jd - 1721424.5) elseif key == 'juliandate' or key == 'jd' or key == 'jdz' then local jd, jdz = julian_date(self) rawset(self, 'juliandate', jd) rawset(self, 'jd', jd) rawset(self, 'jdz', jdz) return key == 'jdz' and jdz or jd elseif key == 'jdnoon' then -- Julian date at noon (an integer) on the calendar day when jd occurs. value = floor(self.jd + 0.5) elseif key == 'isleapyear' then value = is_leap_year(self.year, self.calendar) elseif key == 'monthabbr' then value = month_info[self.month][1] elseif key == 'monthdays' then value = days_in_month(self.year, self.month, self.calendar) elseif key == 'monthname' then value = month_info[self.month][2] end if value ~= nil then rawset(self, key, value) return value end end, } -- Date operators. local function mt_date_add(lhs, rhs) if not is_date(lhs) then lhs, rhs = rhs, lhs -- put date on left (it must be a date for this to have been called) end return date_add_sub(lhs, rhs) end local function mt_date_sub(lhs, rhs) if is_date(lhs) then if is_date(rhs) then return DateDiff(lhs, rhs) end return date_add_sub(lhs, rhs, true) end end local function mt_date_concat(lhs, rhs) return tostring(lhs) .. tostring(rhs) end local function mt_date_tostring(self) return self:text() end local function mt_date_eq(lhs, rhs) -- Return true if dates identify same date/time where, for example, -- Date(-4712, 1, 1, 'Julian') == Date(-4713, 11, 24, 'Gregorian') is true. -- This is called only if lhs and rhs have the same type and the same metamethod. if lhs.partial or rhs.partial then -- One date is partial; the other is a partial or a full date. -- The months may both be nil, but must be the same. return lhs.year == rhs.year and lhs.month == rhs.month and lhs.calendar == rhs.calendar end return lhs.jdz == rhs.jdz end local function mt_date_lt(lhs, rhs) -- Return true if lhs < rhs, for example, -- Date('1 Jan 2016') < Date('06:00 1 Jan 2016') is true. -- This is called only if lhs and rhs have the same type and the same metamethod. if lhs.partial or rhs.partial then -- One date is partial; the other is a partial or a full date. if lhs.calendar ~= rhs.calendar then return lhs.calendar == 'Julian' end if lhs.partial then lhs = lhs.partial.first end if rhs.partial then rhs = rhs.partial.first end end return lhs.jdz < rhs.jdz end --[[ Examples of syntax to construct a date: Date(y, m, d, 'julian') default calendar is 'gregorian' Date(y, m, d, H, M, S, 'julian') Date('juliandate', jd, 'julian') if jd contains "." text output includes H:M:S Date('currentdate') Date('currentdatetime') Date('1 April 1995', 'julian') parse date from text Date('1 April 1995 AD', 'julian') using an era sets a flag to do the same for output Date('04:30:59 1 April 1995', 'julian') Date(date) copy of an existing date Date(date, t) same, updated with y,m,d,H,M,S fields from table t Date(t) date with y,m,d,H,M,S fields from table t ]] function Date(...) -- for forward declaration above -- Return a table holding a date assuming a uniform calendar always applies -- (proleptic Gregorian calendar or proleptic Julian calendar), or -- return nothing if date is invalid. -- A partial date has a valid year, however its month may be nil, and -- its day and time fields are nil. -- Field partial is set to false (if a full date) or a table (if a partial date). local calendars = { julian = 'Julian', gregorian = 'Gregorian' } local newdate = { _id = uniq, calendar = 'Gregorian', -- default is Gregorian calendar hastime = false, -- true if input sets a time hour = 0, -- always set hour/minute/second so don't have to handle nil minute = 0, second = 0, options = {}, list = _date_list, subtract = function (self, rhs, options) return DateDiff(self, rhs, options) end, text = _date_text, } local argtype, datetext, is_copy, jd_number, tnums local numindex = 0 local numfields = { 'year', 'month', 'day', 'hour', 'minute', 'second' } local numbers = {} for _, v in ipairs({...}) do v = strip_to_nil(v) local vlower = type(v) == 'string' and v:lower() or nil if v == nil then -- Ignore empty arguments after stripping so modules can directly pass template parameters. elseif calendars[vlower] then newdate.calendar = calendars[vlower] elseif vlower == 'partial' then newdate.partial = true elseif vlower == 'fix' then newdate.want_fix = true elseif is_date(v) then -- Copy existing date (items can be overridden by other arguments). if is_copy or tnums then return end is_copy = true newdate.calendar = v.calendar newdate.partial = v.partial newdate.hastime = v.hastime newdate.options = v.options newdate.year = v.year newdate.month = v.month newdate.day = v.day newdate.hour = v.hour newdate.minute = v.minute newdate.second = v.second elseif type(v) == 'table' then if tnums then return end tnums = {} local tfields = { year=1, month=1, day=1, hour=2, minute=2, second=2 } for tk, tv in pairs(v) do if tfields[tk] then tnums[tk] = tonumber(tv) end if tfields[tk] == 2 then newdate.hastime = true end end else local num = tonumber(v) if not num and argtype == 'setdate' and numindex == 1 then num = month_number(v) end if num then if not argtype then argtype = 'setdate' end if argtype == 'setdate' and numindex < 6 then numindex = numindex + 1 numbers[numfields[numindex]] = num elseif argtype == 'juliandate' and not jd_number then jd_number = num if type(v) == 'string' then if v:find('.', 1, true) then newdate.hastime = true end elseif num ~= floor(num) then -- The given value was a number. The time will be used -- if the fractional part is nonzero. newdate.hastime = true end else return end elseif argtype then return elseif type(v) == 'string' then if v == 'currentdate' or v == 'currentdatetime' or v == 'juliandate' then argtype = v else argtype = 'datetext' datetext = v end else return end end end if argtype == 'datetext' then if tnums or not set_date_from_numbers(newdate, extract_date(newdate, datetext)) then return end elseif argtype == 'juliandate' then newdate.partial = nil newdate.jd = jd_number if not set_date_from_jd(newdate) then return end elseif argtype == 'currentdate' or argtype == 'currentdatetime' then newdate.partial = nil newdate.year = current.year newdate.month = current.month newdate.day = current.day if argtype == 'currentdatetime' then newdate.hour = current.hour newdate.minute = current.minute newdate.second = current.second newdate.hastime = true end newdate.calendar = 'Gregorian' -- ignore any given calendar name elseif argtype == 'setdate' then if tnums or not set_date_from_numbers(newdate, numbers) then return end elseif not (is_copy or tnums) then return end if tnums then newdate.jd = nil -- force recalculation in case jd was set before changes from tnums if not set_date_from_numbers(newdate, tnums) then return end end if newdate.partial then local year = newdate.year local month = newdate.month local first = Date(year, month or 1, 1, newdate.calendar) month = month or 12 local last = Date(year, month, days_in_month(year, month), newdate.calendar) newdate.partial = { first = first, last = last } else newdate.partial = false -- avoid index lookup end setmetatable(newdate, datemt) local readonly = {} local mt = { __index = newdate, __newindex = function(t, k, v) error('date.' .. tostring(k) .. ' is read-only', 2) end, __add = mt_date_add, __sub = mt_date_sub, __concat = mt_date_concat, __tostring = mt_date_tostring, __eq = mt_date_eq, __lt = mt_date_lt, } return setmetatable(readonly, mt) end local function _diff_age(diff, code, options) -- Return a tuple of integer values from diff as specified by code, except that -- each integer may be a list of two integers for a diff with a partial date, or -- return nil if the code is not supported. -- If want round, the least significant unit is rounded to nearest whole unit. -- For a duration, an extra day is added. local wantround, wantduration, wantrange if type(options) == 'table' then wantround = options.round wantduration = options.duration wantrange = options.range else wantround = options end if not is_diff(diff) then local f = wantduration and 'duration' or 'age' error(f .. ': need a date difference (use "diff:' .. f .. '()" with a colon)', 2) end if diff.partial then -- Ignore wantround, wantduration. local function choose(v) if type(v) == 'table' then if not wantrange or v[1] == v[2] then -- Example: Date('partial', 2005) - Date('partial', 2001) gives -- diff.years = { 3, 4 } to show the range of possible results. -- If do not want a range, choose the second value as more expected. return v[2] end end return v end if code == 'ym' or code == 'ymd' then if not wantrange and diff.iszero then -- This avoids an unexpected result such as -- Date('partial', 2001) - Date('partial', 2001) -- giving diff = { years = 0, months = { 0, 11 } } -- which would be reported as 0 years and 11 months. return 0, 0 end return choose(diff.partial.years), choose(diff.partial.months) end if code == 'y' then return choose(diff.partial.years) end if code == 'm' or code == 'w' or code == 'd' then return choose({ diff.partial.mindiff:age(code), diff.partial.maxdiff:age(code) }) end return nil end local extra_days = wantduration and 1 or 0 if code == 'wd' or code == 'w' or code == 'd' then local offset = wantround and 0.5 or 0 local days = diff.age_days + extra_days if code == 'wd' or code == 'd' then days = floor(days + offset) if code == 'd' then return days end return floor(days/7), days % 7 end return floor(days/7 + offset) end local H, M, S = diff.hours, diff.minutes, diff.seconds if code == 'dh' or code == 'dhm' or code == 'dhms' or code == 'h' or code == 'hm' or code == 'hms' or code == 'M' or code == 's' then local days = floor(diff.age_days + extra_days) local inc_hour if wantround then if code == 'dh' or code == 'h' then if M >= 30 then inc_hour = true end elseif code == 'dhm' or code == 'hm' then if S >= 30 then M = M + 1 if M >= 60 then M = 0 inc_hour = true end end elseif code == 'M' then if S >= 30 then M = M + 1 end else -- Nothing needed because S is an integer. end if inc_hour then H = H + 1 if H >= 24 then H = 0 days = days + 1 end end end if code == 'dh' or code == 'dhm' or code == 'dhms' then if code == 'dh' then return days, H elseif code == 'dhm' then return days, H, M else return days, H, M, S end end local hours = days * 24 + H if code == 'h' then return hours elseif code == 'hm' then return hours, M elseif code == 'M' or code == 's' then M = hours * 60 + M if code == 'M' then return M end return M * 60 + S end return hours, M, S end if wantround then local inc_hour if code == 'ymdh' or code == 'ymwdh' then if M >= 30 then inc_hour = true end elseif code == 'ymdhm' or code == 'ymwdhm' then if S >= 30 then M = M + 1 if M >= 60 then M = 0 inc_hour = true end end elseif code == 'ymd' or code == 'ymwd' or code == 'yd' or code == 'md' then if H >= 12 then extra_days = extra_days + 1 end end if inc_hour then H = H + 1 if H >= 24 then H = 0 extra_days = extra_days + 1 end end end local y, m, d = diff.years, diff.months, diff.days if extra_days > 0 then d = d + extra_days if d > 28 or code == 'yd' then -- Recalculate in case have passed a month. diff = diff.date1 + extra_days - diff.date2 y, m, d = diff.years, diff.months, diff.days end end if code == 'ymd' then return y, m, d elseif code == 'yd' then if y > 0 then -- It is known that diff.date1 > diff.date2. diff = diff.date1 - (diff.date2 + (y .. 'y')) end return y, floor(diff.age_days) elseif code == 'md' then return y * 12 + m, d elseif code == 'ym' or code == 'm' then if wantround then if d >= 16 then m = m + 1 if m >= 12 then m = 0 y = y + 1 end end end if code == 'ym' then return y, m end return y * 12 + m elseif code == 'ymw' then local weeks = floor(d/7) if wantround then local days = d % 7 if days > 3 or (days == 3 and H >= 12) then weeks = weeks + 1 end end return y, m, weeks elseif code == 'ymwd' then return y, m, floor(d/7), d % 7 elseif code == 'ymdh' then return y, m, d, H elseif code == 'ymwdh' then return y, m, floor(d/7), d % 7, H elseif code == 'ymdhm' then return y, m, d, H, M elseif code == 'ymwdhm' then return y, m, floor(d/7), d % 7, H, M end if code == 'y' then if wantround and m >= 6 then y = y + 1 end return y end return nil end local function _diff_duration(diff, code, options) if type(options) ~= 'table' then options = { round = options } end options.duration = true return _diff_age(diff, code, options) end -- Metatable for some operations on date differences. diffmt = { -- for forward declaration above __concat = function (lhs, rhs) return tostring(lhs) .. tostring(rhs) end, __tostring = function (self) return tostring(self.age_days) end, __index = function (self, key) local value if key == 'age_days' then if rawget(self, 'partial') then local function jdz(date) return (date.partial and date.partial.first or date).jdz end value = jdz(self.date1) - jdz(self.date2) else value = self.date1.jdz - self.date2.jdz end end if value ~= nil then rawset(self, key, value) return value end end, } function DateDiff(date1, date2, options) -- for forward declaration above -- Return a table with the difference between two dates (date1 - date2). -- The difference is negative if date1 is older than date2. -- Return nothing if invalid. -- If d = date1 - date2 then -- date1 = date2 + d -- If date1 >= date2 and the dates have no H:M:S time specified then -- date1 = date2 + (d.years..'y') + (d.months..'m') + d.days -- where the larger time units are added first. -- The result of Date(2015,1,x) + '1m' is Date(2015,2,28) for -- x = 28, 29, 30, 31. That means, for example, -- d = Date(2015,3,3) - Date(2015,1,31) -- gives d.years, d.months, d.days = 0, 1, 3 (excluding date1). if not (is_date(date1) and is_date(date2) and date1.calendar == date2.calendar) then return end local wantfill if type(options) == 'table' then wantfill = options.fill end local isnegative = false local iszero = false if date1 < date2 then isnegative = true date1, date2 = date2, date1 elseif date1 == date2 then iszero = true end -- It is known that date1 >= date2 (period is from date2 to date1). if date1.partial or date2.partial then -- Two partial dates might have timelines: ---------------------A=================B--- date1 is from A to B inclusive --------C=======D-------------------------- date2 is from C to D inclusive -- date1 > date2 iff A > C (date1.partial.first > date2.partial.first) -- The periods can overlap ('April 2001' - '2001'): -------------A===B------------------------- A=2001-04-01 B=2001-04-30 --------C=====================D------------ C=2001-01-01 D=2001-12-31 if wantfill then date1, date2 = autofill(date1, date2) else local function zdiff(date1, date2) local diff = date1 - date2 if diff.isnegative then return date1 - date1 -- a valid diff in case we call its methods end return diff end local function getdate(date, which) return date.partial and date.partial[which] or date end local maxdiff = zdiff(getdate(date1, 'last'), getdate(date2, 'first')) local mindiff = zdiff(getdate(date1, 'first'), getdate(date2, 'last')) local years, months if maxdiff.years == mindiff.years then years = maxdiff.years if maxdiff.months == mindiff.months then months = maxdiff.months else months = { mindiff.months, maxdiff.months } end else years = { mindiff.years, maxdiff.years } end return setmetatable({ date1 = date1, date2 = date2, partial = { years = years, months = months, maxdiff = maxdiff, mindiff = mindiff, }, isnegative = isnegative, iszero = iszero, age = _diff_age, duration = _diff_duration, }, diffmt) end end local y1, m1 = date1.year, date1.month local y2, m2 = date2.year, date2.month local years = y1 - y2 local months = m1 - m2 local d1 = date1.day + hms(date1) local d2 = date2.day + hms(date2) local days, time if d1 >= d2 then days = d1 - d2 else months = months - 1 -- Get days in previous month (before the "to" date) given December has 31 days. local dpm = m1 > 1 and days_in_month(y1, m1 - 1, date1.calendar) or 31 if d2 >= dpm then days = d1 - hms(date2) else days = dpm - d2 + d1 end end if months < 0 then years = years - 1 months = months + 12 end days, time = math.modf(days) local H, M, S = h_m_s(time) return setmetatable({ date1 = date1, date2 = date2, partial = false, -- avoid index lookup years = years, months = months, days = days, hours = H, minutes = M, seconds = S, isnegative = isnegative, iszero = iszero, age = _diff_age, duration = _diff_duration, }, diffmt) end return { _current = current, _Date = Date, _days_in_month = days_in_month, } 48b9402c32798b1e9f91f2ab44283ebda7b53ed9 Module:Navbar 828 440 881 880 2023-07-10T15:58:08Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Navbar]] Scribunto text/plain local p = {} local cfg = mw.loadData('Module:Navbar/configuration') local function get_title_arg(is_collapsible, template) local title_arg = 1 if is_collapsible then title_arg = 2 end if template then title_arg = 'template' end return title_arg end local function choose_links(template, args) -- The show table indicates the default displayed items. -- view, talk, edit, hist, move, watch -- TODO: Move to configuration. local show = {true, true, true, false, false, false} if template then show[2] = false show[3] = false local index = {t = 2, d = 2, e = 3, h = 4, m = 5, w = 6, talk = 2, edit = 3, hist = 4, move = 5, watch = 6} -- TODO: Consider removing TableTools dependency. for _, v in ipairs(require ('Module:TableTools').compressSparseArray(args)) do local num = index[v] if num then show[num] = true end end end local remove_edit_link = args.noedit if remove_edit_link then show[3] = false end return show end local function add_link(link_description, ul, is_mini, font_style) local l if link_description.url then l = {'[', '', ']'} else l = {'[[', '|', ']]'} end ul:tag('li') :addClass('nv-' .. link_description.full) :wikitext(l[1] .. link_description.link .. l[2]) :tag(is_mini and 'abbr' or 'span') :attr('title', link_description.html_title) :cssText(font_style) :wikitext(is_mini and link_description.mini or link_description.full) :done() :wikitext(l[3]) :done() end local function make_list(title_text, has_brackets, displayed_links, is_mini, font_style) local title = mw.title.new(mw.text.trim(title_text), cfg.title_namespace) if not title then error(cfg.invalid_title .. title_text) end local talkpage = title.talkPageTitle and title.talkPageTitle.fullText or '' -- TODO: Get link_descriptions and show into the configuration module. -- link_descriptions should be easier... local link_descriptions = { { ['mini'] = 'v', ['full'] = 'view', ['html_title'] = 'View this template', ['link'] = title.fullText, ['url'] = false }, { ['mini'] = 't', ['full'] = 'talk', ['html_title'] = 'Discuss this template', ['link'] = talkpage, ['url'] = false }, { ['mini'] = 'e', ['full'] = 'edit', ['html_title'] = 'Edit this template', ['link'] = title:fullUrl('action=edit'), ['url'] = true }, { ['mini'] = 'h', ['full'] = 'hist', ['html_title'] = 'History of this template', ['link'] = title:fullUrl('action=history'), ['url'] = true }, { ['mini'] = 'm', ['full'] = 'move', ['html_title'] = 'Move this template', ['link'] = mw.title.new('Special:Movepage'):fullUrl('target='..title.fullText), ['url'] = true }, { ['mini'] = 'w', ['full'] = 'watch', ['html_title'] = 'Watch this template', ['link'] = title:fullUrl('action=watch'), ['url'] = true } } local ul = mw.html.create('ul') if has_brackets then ul:addClass(cfg.classes.brackets) :cssText(font_style) end for i, _ in ipairs(displayed_links) do if displayed_links[i] then add_link(link_descriptions[i], ul, is_mini, font_style) end end return ul:done() end function p._navbar(args) -- TODO: We probably don't need both fontstyle and fontcolor... local font_style = args.fontstyle local font_color = args.fontcolor local is_collapsible = args.collapsible local is_mini = args.mini local is_plain = args.plain local collapsible_class = nil if is_collapsible then collapsible_class = cfg.classes.collapsible if not is_plain then is_mini = 1 end if font_color then font_style = (font_style or '') .. '; color: ' .. font_color .. ';' end end local navbar_style = args.style local div = mw.html.create():tag('div') div :addClass(cfg.classes.navbar) :addClass(cfg.classes.plainlinks) :addClass(cfg.classes.horizontal_list) :addClass(collapsible_class) -- we made the determination earlier :cssText(navbar_style) if is_mini then div:addClass(cfg.classes.mini) end local box_text = (args.text or cfg.box_text) .. ' ' -- the concatenated space guarantees the box text is separated if not (is_mini or is_plain) then div :tag('span') :addClass(cfg.classes.box_text) :cssText(font_style) :wikitext(box_text) end local template = args.template local displayed_links = choose_links(template, args) local has_brackets = args.brackets local title_arg = get_title_arg(is_collapsible, template) local title_text = args[title_arg] or (':' .. mw.getCurrentFrame():getParent():getTitle()) local list = make_list(title_text, has_brackets, displayed_links, is_mini, font_style) div:node(list) if is_collapsible then local title_text_class if is_mini then title_text_class = cfg.classes.collapsible_title_mini else title_text_class = cfg.classes.collapsible_title_full end div:done() :tag('div') :addClass(title_text_class) :cssText(font_style) :wikitext(args[1]) end local frame = mw.getCurrentFrame() -- hlist -> navbar is best-effort to preserve old Common.css ordering. return frame:extensionTag{ name = 'templatestyles', args = { src = cfg.hlist_templatestyles } } .. frame:extensionTag{ name = 'templatestyles', args = { src = cfg.templatestyles } } .. tostring(div:done()) end function p.navbar(frame) return p._navbar(require('Module:Arguments').getArgs(frame)) end return p 79f907e59eaa8bbf8dd50bb751933ebeaaa7eb17 Module:Navbar/configuration 828 441 883 882 2023-07-10T15:58:09Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Navbar/configuration]] Scribunto text/plain return { ['templatestyles'] = 'Module:Navbar/styles.css', ['hlist_templatestyles'] = 'Hlist/styles.css', ['box_text'] = 'This box: ', -- default text box when not plain or mini ['title_namespace'] = 'Template', -- namespace to default to for title ['invalid_title'] = 'Invalid title ', ['classes'] = { -- set a line to nil if you don't want it ['navbar'] = 'navbar', ['plainlinks'] = 'plainlinks', -- plainlinks ['horizontal_list'] = 'hlist', -- horizontal list class ['mini'] = 'navbar-mini', -- class indicating small links in the navbar ['this_box'] = 'navbar-boxtext', ['brackets'] = 'navbar-brackets', -- 'collapsible' is the key for a class to indicate the navbar is -- setting up the collapsible element in addition to the normal -- navbar. ['collapsible'] = 'navbar-collapse', ['collapsible_title_mini'] = 'navbar-ct-mini', ['collapsible_title_full'] = 'navbar-ct-full' } } b007c336b17ec4bcd4d5a9dca9f8cba301662b55 Module:Navbar/styles.css 828 442 885 884 2023-07-10T15:58:09Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Navbar/styles.css]] text text/plain /* {{pp|small=yes}} */ .navbar { display: inline; font-size: 88%; font-weight: normal; } .navbar-collapse { float: left; text-align: left; } .navbar-boxtext { word-spacing: 0; } .navbar ul { display: inline-block; white-space: nowrap; line-height: inherit; } .navbar-brackets::before { margin-right: -0.125em; content: '[ '; } .navbar-brackets::after { margin-left: -0.125em; content: ' ]'; } .navbar li { word-spacing: -0.125em; } .navbar a > span, .navbar a > abbr { text-decoration: inherit; } .navbar-mini abbr { font-variant: small-caps; border-bottom: none; text-decoration: none; cursor: inherit; } .navbar-ct-full { font-size: 114%; margin: 0 7em; } .navbar-ct-mini { font-size: 114%; margin: 0 4em; } 9d4056f949b4f0b159e3d40dfb1a5f01e72f9571 Module:Navbox 828 443 887 886 2023-07-10T15:58:09Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Navbox]] Scribunto text/plain require('strict') local p = {} local navbar = require('Module:Navbar')._navbar local cfg = mw.loadData('Module:Navbox/configuration') local getArgs -- lazily initialized local args local format = string.format local function striped(wikitext, border) -- Return wikitext with markers replaced for odd/even striping. -- Child (subgroup) navboxes are flagged with a category that is removed -- by parent navboxes. The result is that the category shows all pages -- where a child navbox is not contained in a parent navbox. local orphanCat = cfg.category.orphan if border == cfg.keyword.border_subgroup and args[cfg.arg.orphan] ~= cfg.keyword.orphan_yes then -- No change; striping occurs in outermost navbox. return wikitext .. orphanCat end local first, second = cfg.class.navbox_odd_part, cfg.class.navbox_even_part if args[cfg.arg.evenodd] then if args[cfg.arg.evenodd] == cfg.keyword.evenodd_swap then first, second = second, first else first = args[cfg.arg.evenodd] second = first end end local changer if first == second then changer = first else local index = 0 changer = function (code) if code == '0' then -- Current occurrence is for a group before a nested table. -- Set it to first as a valid although pointless class. -- The next occurrence will be the first row after a title -- in a subgroup and will also be first. index = 0 return first end index = index + 1 return index % 2 == 1 and first or second end end local regex = orphanCat:gsub('([%[%]])', '%%%1') return (wikitext:gsub(regex, ''):gsub(cfg.marker.regex, changer)) -- () omits gsub count end local function processItem(item, nowrapitems) if item:sub(1, 2) == '{|' then -- Applying nowrap to lines in a table does not make sense. -- Add newlines to compensate for trim of x in |parm=x in a template. return '\n' .. item ..'\n' end if nowrapitems == cfg.keyword.nowrapitems_yes then local lines = {} for line in (item .. '\n'):gmatch('([^\n]*)\n') do local prefix, content = line:match('^([*:;#]+)%s*(.*)') if prefix and not content:match(cfg.pattern.nowrap) then line = format(cfg.nowrap_item, prefix, content) end table.insert(lines, line) end item = table.concat(lines, '\n') end if item:match('^[*:;#]') then return '\n' .. item ..'\n' end return item end local function has_navbar() return args[cfg.arg.navbar] ~= cfg.keyword.navbar_off and args[cfg.arg.navbar] ~= cfg.keyword.navbar_plain and ( args[cfg.arg.name] or mw.getCurrentFrame():getParent():getTitle():gsub(cfg.pattern.sandbox, '') ~= cfg.pattern.navbox ) end local function renderNavBar(titleCell) if has_navbar() then titleCell:wikitext(navbar{ [cfg.navbar.name] = args[cfg.arg.name], [cfg.navbar.mini] = 1, [cfg.navbar.fontstyle] = (args[cfg.arg.basestyle] or '') .. ';' .. (args[cfg.arg.titlestyle] or '') .. ';background:none transparent;border:none;box-shadow:none;padding:0;' }) end end local function renderTitleRow(tbl) if not args[cfg.arg.title] then return end local titleRow = tbl:tag('tr') local titleCell = titleRow:tag('th'):attr('scope', 'col') local titleColspan = 2 if args[cfg.arg.imageleft] then titleColspan = titleColspan + 1 end if args[cfg.arg.image] then titleColspan = titleColspan + 1 end titleCell :cssText(args[cfg.arg.basestyle]) :cssText(args[cfg.arg.titlestyle]) :addClass(cfg.class.navbox_title) :attr('colspan', titleColspan) renderNavBar(titleCell) titleCell :tag('div') -- id for aria-labelledby attribute :attr('id', mw.uri.anchorEncode(args[cfg.arg.title])) :addClass(args[cfg.arg.titleclass]) :css('font-size', '114%') :css('margin', '0 4em') :wikitext(processItem(args[cfg.arg.title])) end local function getAboveBelowColspan() local ret = 2 if args[cfg.arg.imageleft] then ret = ret + 1 end if args[cfg.arg.image] then ret = ret + 1 end return ret end local function renderAboveRow(tbl) if not args[cfg.arg.above] then return end tbl:tag('tr') :tag('td') :addClass(cfg.class.navbox_abovebelow) :addClass(args[cfg.arg.aboveclass]) :cssText(args[cfg.arg.basestyle]) :cssText(args[cfg.arg.abovestyle]) :attr('colspan', getAboveBelowColspan()) :tag('div') -- id for aria-labelledby attribute, if no title :attr('id', (not args[cfg.arg.title]) and mw.uri.anchorEncode(args[cfg.arg.above]) or nil) :wikitext(processItem(args[cfg.arg.above], args[cfg.arg.nowrapitems])) end local function renderBelowRow(tbl) if not args[cfg.arg.below] then return end tbl:tag('tr') :tag('td') :addClass(cfg.class.navbox_abovebelow) :addClass(args[cfg.arg.belowclass]) :cssText(args[cfg.arg.basestyle]) :cssText(args[cfg.arg.belowstyle]) :attr('colspan', getAboveBelowColspan()) :tag('div') :wikitext(processItem(args[cfg.arg.below], args[cfg.arg.nowrapitems])) end local function renderListRow(tbl, index, listnum, listnums_size) local row = tbl:tag('tr') if index == 1 and args[cfg.arg.imageleft] then row :tag('td') :addClass(cfg.class.noviewer) :addClass(cfg.class.navbox_image) :addClass(args[cfg.arg.imageclass]) :css('width', '1px') -- Minimize width :css('padding', '0 2px 0 0') :cssText(args[cfg.arg.imageleftstyle]) :attr('rowspan', listnums_size) :tag('div') :wikitext(processItem(args[cfg.arg.imageleft])) end local group_and_num = format(cfg.arg.group_and_num, listnum) local groupstyle_and_num = format(cfg.arg.groupstyle_and_num, listnum) if args[group_and_num] then local groupCell = row:tag('th') -- id for aria-labelledby attribute, if lone group with no title or above if listnum == 1 and not (args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group2]) then groupCell :attr('id', mw.uri.anchorEncode(args[cfg.arg.group1])) end groupCell :attr('scope', 'row') :addClass(cfg.class.navbox_group) :addClass(args[cfg.arg.groupclass]) :cssText(args[cfg.arg.basestyle]) -- If groupwidth not specified, minimize width :css('width', args[cfg.arg.groupwidth] or '1%') groupCell :cssText(args[cfg.arg.groupstyle]) :cssText(args[groupstyle_and_num]) :wikitext(args[group_and_num]) end local listCell = row:tag('td') if args[group_and_num] then listCell :addClass(cfg.class.navbox_list_with_group) else listCell:attr('colspan', 2) end if not args[cfg.arg.groupwidth] then listCell:css('width', '100%') end local rowstyle -- usually nil so cssText(rowstyle) usually adds nothing if index % 2 == 1 then rowstyle = args[cfg.arg.oddstyle] else rowstyle = args[cfg.arg.evenstyle] end local list_and_num = format(cfg.arg.list_and_num, listnum) local listText = args[list_and_num] local oddEven = cfg.marker.oddeven if listText:sub(1, 12) == '</div><table' then -- Assume list text is for a subgroup navbox so no automatic striping for this row. oddEven = listText:find(cfg.pattern.navbox_title) and cfg.marker.restart or cfg.class.navbox_odd_part end local liststyle_and_num = format(cfg.arg.liststyle_and_num, listnum) local listclass_and_num = format(cfg.arg.listclass_and_num, listnum) listCell :css('padding', '0') :cssText(args[cfg.arg.liststyle]) :cssText(rowstyle) :cssText(args[liststyle_and_num]) :addClass(cfg.class.navbox_list) :addClass(cfg.class.navbox_part .. oddEven) :addClass(args[cfg.arg.listclass]) :addClass(args[listclass_and_num]) :tag('div') :css('padding', (index == 1 and args[cfg.arg.list1padding]) or args[cfg.arg.listpadding] or '0 0.25em' ) :wikitext(processItem(listText, args[cfg.arg.nowrapitems])) if index == 1 and args[cfg.arg.image] then row :tag('td') :addClass(cfg.class.noviewer) :addClass(cfg.class.navbox_image) :addClass(args[cfg.arg.imageclass]) :css('width', '1px') -- Minimize width :css('padding', '0 0 0 2px') :cssText(args[cfg.arg.imagestyle]) :attr('rowspan', listnums_size) :tag('div') :wikitext(processItem(args[cfg.arg.image])) end end local function has_list_class(htmlclass) local patterns = { '^' .. htmlclass .. '$', '%s' .. htmlclass .. '$', '^' .. htmlclass .. '%s', '%s' .. htmlclass .. '%s' } for arg, _ in pairs(args) do if type(arg) == 'string' and mw.ustring.find(arg, cfg.pattern.class) then for _, pattern in ipairs(patterns) do if mw.ustring.find(args[arg] or '', pattern) then return true end end end end return false end -- there are a lot of list classes in the wild, so we add their TemplateStyles local function add_list_styles() local frame = mw.getCurrentFrame() local function add_list_templatestyles(htmlclass, templatestyles) if has_list_class(htmlclass) then return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } else return '' end end local hlist_styles = add_list_templatestyles('hlist', cfg.hlist_templatestyles) local plainlist_styles = add_list_templatestyles('plainlist', cfg.plainlist_templatestyles) -- a second workaround for [[phab:T303378]] -- when that issue is fixed, we can actually use has_navbar not to emit the -- tag here if we want if has_navbar() and hlist_styles == '' then hlist_styles = frame:extensionTag{ name = 'templatestyles', args = { src = cfg.hlist_templatestyles } } end -- hlist -> plainlist is best-effort to preserve old Common.css ordering. -- this ordering is not a guarantee because most navboxes will emit only -- one of these classes [hlist_note] return hlist_styles .. plainlist_styles end local function needsHorizontalLists(border) if border == cfg.keyword.border_subgroup or args[cfg.arg.tracking] == cfg.keyword.tracking_no then return false end return not has_list_class(cfg.pattern.hlist) and not has_list_class(cfg.pattern.plainlist) end local function hasBackgroundColors() for _, key in ipairs({cfg.arg.titlestyle, cfg.arg.groupstyle, cfg.arg.basestyle, cfg.arg.abovestyle, cfg.arg.belowstyle}) do if tostring(args[key]):find('background', 1, true) then return true end end return false end local function hasBorders() for _, key in ipairs({cfg.arg.groupstyle, cfg.arg.basestyle, cfg.arg.abovestyle, cfg.arg.belowstyle}) do if tostring(args[key]):find('border', 1, true) then return true end end return false end local function isIllegible() local styleratio = require('Module:Color contrast')._styleratio for key, style in pairs(args) do if tostring(key):match(cfg.pattern.style) then if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then return true end end end return false end local function getTrackingCategories(border) local cats = {} if needsHorizontalLists(border) then table.insert(cats, cfg.category.horizontal_lists) end if hasBackgroundColors() then table.insert(cats, cfg.category.background_colors) end if isIllegible() then table.insert(cats, cfg.category.illegible) end if hasBorders() then table.insert(cats, cfg.category.borders) end return cats end local function renderTrackingCategories(builder, border) local title = mw.title.getCurrentTitle() if title.namespace ~= 10 then return end -- not in template space local subpage = title.subpageText if subpage == cfg.keyword.subpage_doc or subpage == cfg.keyword.subpage_sandbox or subpage == cfg.keyword.subpage_testcases then return end for _, cat in ipairs(getTrackingCategories(border)) do builder:wikitext('[[Category:' .. cat .. ']]') end end local function renderMainTable(border, listnums) local tbl = mw.html.create('table') :addClass(cfg.class.nowraplinks) :addClass(args[cfg.arg.bodyclass]) local state = args[cfg.arg.state] if args[cfg.arg.title] and state ~= cfg.keyword.state_plain and state ~= cfg.keyword.state_off then if state == cfg.keyword.state_collapsed then state = cfg.class.collapsed end tbl :addClass(cfg.class.collapsible) :addClass(state or cfg.class.autocollapse) end tbl:css('border-spacing', 0) if border == cfg.keyword.border_subgroup or border == cfg.keyword.border_none then tbl :addClass(cfg.class.navbox_subgroup) :cssText(args[cfg.arg.bodystyle]) :cssText(args[cfg.arg.style]) else -- regular navbox - bodystyle and style will be applied to the wrapper table tbl :addClass(cfg.class.navbox_inner) :css('background', 'transparent') :css('color', 'inherit') end tbl:cssText(args[cfg.arg.innerstyle]) renderTitleRow(tbl) renderAboveRow(tbl) local listnums_size = #listnums for i, listnum in ipairs(listnums) do renderListRow(tbl, i, listnum, listnums_size) end renderBelowRow(tbl) return tbl end local function add_navbox_styles(hiding_templatestyles) local frame = mw.getCurrentFrame() -- This is a lambda so that it doesn't need the frame as a parameter local function add_user_styles(templatestyles) if templatestyles and templatestyles ~= '' then return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } end return '' end -- get templatestyles. load base from config so that Lua only needs to do -- the work once of parser tag expansion local base_templatestyles = cfg.templatestyles local templatestyles = add_user_styles(args[cfg.arg.templatestyles]) local child_templatestyles = add_user_styles(args[cfg.arg.child_templatestyles]) -- The 'navbox-styles' div exists to wrap the styles to work around T200206 -- more elegantly. Instead of combinatorial rules, this ends up being linear -- number of CSS rules. return mw.html.create('div') :addClass(cfg.class.navbox_styles) :wikitext( add_list_styles() .. -- see [hlist_note] applied to 'before base_templatestyles' base_templatestyles .. templatestyles .. child_templatestyles .. table.concat(hiding_templatestyles) ) :done() end -- work around [[phab:T303378]] -- for each arg: find all the templatestyles strip markers, insert them into a -- table. then remove all templatestyles markers from the arg local function move_hiding_templatestyles(args) local gfind = string.gfind local gsub = string.gsub local templatestyles_markers = {} local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)' for k, arg in pairs(args) do for marker in gfind(arg, strip_marker_pattern) do table.insert(templatestyles_markers, marker) end args[k] = gsub(arg, strip_marker_pattern, '') end return templatestyles_markers end function p._navbox(navboxArgs) args = navboxArgs local hiding_templatestyles = move_hiding_templatestyles(args) local listnums = {} for k, _ in pairs(args) do if type(k) == 'string' then local listnum = k:match(cfg.pattern.listnum) if listnum then table.insert(listnums, tonumber(listnum)) end end end table.sort(listnums) local border = mw.text.trim(args[cfg.arg.border] or args[1] or '') if border == cfg.keyword.border_child then border = cfg.keyword.border_subgroup end -- render the main body of the navbox local tbl = renderMainTable(border, listnums) local res = mw.html.create() -- render the appropriate wrapper for the navbox, based on the border param if border == cfg.keyword.border_none then res:node(add_navbox_styles(hiding_templatestyles)) local nav = res:tag('div') :attr('role', 'navigation') :node(tbl) -- aria-labelledby title, otherwise above, otherwise lone group if args[cfg.arg.title] or args[cfg.arg.above] or (args[cfg.arg.group1] and not args[cfg.arg.group2]) then nav:attr( 'aria-labelledby', mw.uri.anchorEncode( args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group1] ) ) else nav:attr('aria-label', cfg.aria_label) end elseif border == cfg.keyword.border_subgroup then -- We assume that this navbox is being rendered in a list cell of a -- parent navbox, and is therefore inside a div with padding:0em 0.25em. -- We start with a </div> to avoid the padding being applied, and at the -- end add a <div> to balance out the parent's </div> res :wikitext('</div>') :node(tbl) :wikitext('<div>') else res:node(add_navbox_styles(hiding_templatestyles)) local nav = res:tag('div') :attr('role', 'navigation') :addClass(cfg.class.navbox) :addClass(args[cfg.arg.navboxclass]) :cssText(args[cfg.arg.bodystyle]) :cssText(args[cfg.arg.style]) :css('padding', '3px') :node(tbl) -- aria-labelledby title, otherwise above, otherwise lone group if args[cfg.arg.title] or args[cfg.arg.above] or (args[cfg.arg.group1] and not args[cfg.arg.group2]) then nav:attr( 'aria-labelledby', mw.uri.anchorEncode(args[cfg.arg.title] or args[cfg.arg.above] or args[cfg.arg.group1]) ) else nav:attr('aria-label', cfg.aria_label) end end if (args[cfg.arg.nocat] or cfg.keyword.nocat_false):lower() == cfg.keyword.nocat_false then renderTrackingCategories(res, border) end return striped(tostring(res), border) end function p.navbox(frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end args = getArgs(frame, {wrappers = {cfg.pattern.navbox}}) -- Read the arguments in the order they'll be output in, to make references -- number in the right order. local _ _ = args[cfg.arg.title] _ = args[cfg.arg.above] -- Limit this to 20 as covering 'most' cases (that's a SWAG) and because -- iterator approach won't work here for i = 1, 20 do _ = args[format(cfg.arg.group_and_num, i)] _ = args[format(cfg.arg.list_and_num, i)] end _ = args[cfg.arg.below] return p._navbox(args) end return p 05be9a97c035ab3f0fac69423779e261949d473c Module:Navbox/configuration 828 444 889 888 2023-07-10T15:58:10Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Navbox/configuration]] Scribunto text/plain return { aria_label = 'Navbox', nowrap_item = '%s<span class="nowrap">%s</span>', templatestyles = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Module:Navbox/styles.css' } }, hlist_templatestyles = 'Hlist/styles.css', plainlist_templatestyles = 'Plainlist/styles.css', -- do not localize marker table marker = { oddeven = '\127_ODDEVEN_\127', restart = '\127_ODDEVEN0_\127', regex = '\127_ODDEVEN(%d?)_\127' }, category = { orphan = '[[Category:Navbox orphans]]', horizontal_lists = 'Navigational boxes without horizontal lists', background_colors = 'Navboxes using background colours', illegible = 'Potentially illegible navboxes', borders = 'Navboxes using borders', }, keyword = { border_subgroup = 'subgroup', border_child = 'child', border_none = 'none', evenodd_swap = 'swap', navbar_off = 'off', navbar_plain = 'plain', nocat_false = 'false', nowrapitems_yes = 'yes', orphan_yes = 'yes', state_collapsed = 'collapsed', state_off = 'off', state_plain = 'plain', subpage_doc = 'doc', subpage_sandbox = 'sandbox', subpage_testcases = 'testcases', tracking_no = 'no' }, class = { autocollapse = 'autocollapse', collapsible = 'mw-collapsible', collapsed = 'mw-collapsed', -- Warning navbox = 'navbox', -- WMF currently hides 'navbox' from mobile, -- so you probably shouldn't change the navbox class. navbox_abovebelow = 'navbox-abovebelow', navbox_group = 'navbox-group', navbox_image = 'navbox-image', navbox_inner = 'navbox-inner', navbox_list = 'navbox-list', navbox_list_with_group = 'navbox-list-with-group', navbox_part = 'navbox-', -- do not l10n navbox_styles = 'navbox-styles', navbox_subgroup = 'navbox-subgroup', navbox_title = 'navbox-title', -- l10n only if you change pattern.navbox_title below navbox_odd_part = 'odd', -- do not l10n navbox_even_part = 'even', -- do not l10n nomobile = 'nomobile', nowraplinks = 'nowraplinks', noviewer = 'noviewer' -- used to remove images from MediaViewer }, pattern = { listnum = '^list(%d+)$', class = 'class', sandbox = '/sandbox$', navbox = 'Template:Navbox', nowrap = '^<span class="nowrap">', style = 'style$', navbox_title = '<th[^>]*"navbox%-title"', hlist = 'hlist', plainlist = 'plainlist', }, arg = { above = 'above', aboveclass = 'aboveclass', abovestyle = 'abovestyle', basestyle = 'basestyle', bodyclass = 'bodyclass', bodystyle = 'bodystyle', border = 'border', below = 'below', belowclass = 'belowclass', belowstyle = 'belowstyle', evenodd = 'evenodd', evenstyle = 'evenstyle', group1 = 'group1', group2 = 'group2', group_and_num = 'group%d', groupstyle_and_num = 'group%dstyle', groupclass = 'groupclass', groupstyle = 'groupstyle', groupwidth = 'groupwidth', innerstyle = 'innerstyle', image = 'image', imageclass = 'imageclass', imageleft = 'imageleft', imageleftstyle = 'imageleftstyle', imagesetyle = 'imagestyle', list_and_num = 'list%d', listclass_and_num = 'list%dclass', liststyle_and_num = 'list%dstyle', list1padding = 'list1padding', listclass = 'listclass', listpadding = 'listpadding', liststyle = 'liststyle', name = 'name', navbar = 'navbar', navboxclass = 'navboxclass', nocat = 'nocat', nowrapitems = 'nowrapitems', oddstyle = 'oddstyle', orphan = 'orphan', state = 'state', style = 'style', templatestyles = 'templatestyles', child_templatestyles = 'child templatestyles', title = 'title', titleclass = 'titleclass', titlestyle = 'titlestyle', tracking = 'tracking' }, -- names of navbar arguments navbar = { name = 1, fontstyle = 'fontstyle', mini = 'mini' } } 4148736fd32a93636c0413e73ed38afaef065ec9 Module:Navbox/styles.css 828 445 891 890 2023-07-10T15:58:10Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Navbox/styles.css]] text text/plain /* {{pp|small=y}} */ .navbox { box-sizing: border-box; border: 1px solid #a2a9b1; width: 100%; clear: both; font-size: 88%; text-align: center; padding: 1px; margin: 1em auto 0; /* Prevent preceding content from clinging to navboxes */ } .navbox .navbox { margin-top: 0; /* No top margin for nested navboxes */ } .navbox + .navbox, /* TODO: remove first line after transclusions have updated */ .navbox + .navbox-styles + .navbox { margin-top: -1px; /* Single pixel border between adjacent navboxes */ } .navbox-inner, .navbox-subgroup { width: 100%; } .navbox-group, .navbox-title, .navbox-abovebelow { padding: 0.25em 1em; line-height: 1.5em; text-align: center; } .navbox-group { white-space: nowrap; /* @noflip */ text-align: right; } .navbox, .navbox-subgroup { background-color: #fdfdfd; } .navbox-list { line-height: 1.5em; border-color: #fdfdfd; /* Must match background color */ } .navbox-list-with-group { text-align: left; border-left-width: 2px; border-left-style: solid; } /* cell spacing for navbox cells */ /* Borders above 2nd, 3rd, etc. rows */ /* TODO: figure out how to replace tr as structure; * with div structure it should be just a matter of first-child */ tr + tr > .navbox-abovebelow, tr + tr > .navbox-group, tr + tr > .navbox-image, tr + tr > .navbox-list { border-top: 2px solid #fdfdfd; /* Must match background color */ } .navbox-title { background-color: #ccf; /* Level 1 color */ } .navbox-abovebelow, .navbox-group, .navbox-subgroup .navbox-title { background-color: #ddf; /* Level 2 color */ } .navbox-subgroup .navbox-group, .navbox-subgroup .navbox-abovebelow { background-color: #e6e6ff; /* Level 3 color */ } .navbox-even { background-color: #f7f7f7; } .navbox-odd { background-color: transparent; } /* TODO: figure out how to remove reliance on td as structure */ .navbox .hlist td dl, .navbox .hlist td ol, .navbox .hlist td ul, .navbox td.hlist dl, .navbox td.hlist ol, .navbox td.hlist ul { padding: 0.125em 0; } .navbox .navbar { display: block; font-size: 100%; } .navbox-title .navbar { /* @noflip */ float: left; /* @noflip */ text-align: left; /* @noflip */ margin-right: 0.5em; } e80b0d7a5770e6e105dab832deb6c37a5245ebc6 Template:Trim 10 446 893 892 2023-07-10T15:58:10Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Trim]] wikitext text/x-wiki <includeonly>{{safesubst:#if:1|{{{1|}}}}}</includeonly><noinclude> {{Documentation}} </noinclude> 3d29fbfff9683523147db6e1f55c0e17ed30863b Module:WikidataIB 828 447 895 894 2023-07-10T15:58:11Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:WikidataIB]] Scribunto text/plain -- Version: 2021-02-06 -- Module to implement use of a blacklist and whitelist for infobox fields -- Can take a named parameter |qid which is the Wikidata ID for the article -- if not supplied, it will use the Wikidata ID associated with the current page. -- Fields in blacklist are never to be displayed, i.e. module must return nil in all circumstances -- Fields in whitelist return local value if it exists or the Wikidata value otherwise -- The name of the field that this function is called from is passed in named parameter |name -- The name is compulsory when blacklist or whitelist is used, -- so the module returns nil if it is not supplied. -- blacklist is passed in named parameter |suppressfields (or |spf) -- whitelist is passed in named parameter |fetchwikidata (or |fwd) require("strict") local p = {} local cdate -- initialise as nil and only load _complex_date function if needed -- Module:Complex date is loaded lazily and has the following dependencies: -- Module:Calendar -- Module:ISOdate -- Module:DateI18n -- Module:I18n/complex date -- Module:Ordinal -- Module:I18n/ordinal -- Module:Yesno -- Module:Formatnum -- Module:Linguistic -- -- The following, taken from https://www.mediawiki.org/wiki/Wikibase/DataModel#Dates_and_times, -- is needed to use Module:Complex date which seemingly requires date precision as a string. -- It would work better if only the authors of the mediawiki page could spell 'millennium'. local dp = { [6] = "millennium", [7] = "century", [8] = "decade", [9] = "year", [10] = "month", [11] = "day", } local i18n = { ["errors"] = { ["property-not-found"] = "Property not found.", ["No property supplied"] = "No property supplied", ["entity-not-found"] = "Wikidata entity not found.", ["unknown-claim-type"] = "Unknown claim type.", ["unknown-entity-type"] = "Unknown entity type.", ["qualifier-not-found"] = "Qualifier not found.", ["site-not-found"] = "Wikimedia project not found.", ["labels-not-found"] = "No labels found.", ["descriptions-not-found"] = "No descriptions found.", ["aliases-not-found"] = "No aliases found.", ["unknown-datetime-format"] = "Unknown datetime format.", ["local-article-not-found"] = "Article is available on Wikidata, but not on Wikipedia", ["dab-page"] = " (dab)", }, ["months"] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }, ["century"] = "century", ["BC"] = "BC", ["BCE"] = "BCE", ["ordinal"] = { [1] = "st", [2] = "nd", [3] = "rd", ["default"] = "th" }, ["filespace"] = "File", ["Unknown"] = "Unknown", ["NaN"] = "Not a number", -- set the following to the name of a tracking category, -- e.g. "[[Category:Articles with missing Wikidata information]]", or "" to disable: ["missinginfocat"] = "[[Category:Articles with missing Wikidata information]]", ["editonwikidata"] = "Edit this on Wikidata", ["latestdatequalifier"] = function (date) return "before " .. date end, -- some languages, e.g. Bosnian use a period as a suffix after each number in a date ["datenumbersuffix"] = "", ["list separator"] = ", ", ["multipliers"] = { [0] = "", [3] = " thousand", [6] = " million", [9] = " billion", [12] = " trillion", } } -- This allows an internationisation module to override the above table if 'en' ~= mw.getContentLanguage():getCode() then require("Module:i18n").loadI18n("Module:WikidataIB/i18n", i18n) end -- This piece of html implements a collapsible container. Check the classes exist on your wiki. local collapsediv = '<div class="mw-collapsible mw-collapsed" style="width:100%; overflow:auto;" data-expandtext="{{int:show}}" data-collapsetext="{{int:hide}}">' -- Some items should not be linked. -- Each wiki can create a list of those in Module:WikidataIB/nolinks -- It should return a table called itemsindex, containing true for each item not to be linked local donotlink = {} local nolinks_exists, nolinks = pcall(mw.loadData, "Module:WikidataIB/nolinks") if nolinks_exists then donotlink = nolinks.itemsindex end -- To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised, and others are quoted. -- The submodule [[Module:WikidataIB/titleformats]] lists the entity-ids used in 'instance of' (P31), -- which allows this module to identify the values that should be formatted. -- WikidataIB/titleformats exports a table p.formats, which is indexed by entity-id, and contains the value " or '' local formats = {} local titleformats_exists, titleformats = pcall(mw.loadData, "Module:WikidataIB/titleformats") if titleformats_exists then formats = titleformats.formats end ------------------------------------------------------------------------------- -- Private functions ------------------------------------------------------------------------------- -- ------------------------------------------------------------------------------- -- makeOrdinal needs to be internationalised along with the above: -- takes cardinal number as a numeric and returns the ordinal as a string -- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local makeOrdinal = function(cardinal) local ordsuffix = i18n.ordinal.default if cardinal % 10 == 1 then ordsuffix = i18n.ordinal[1] elseif cardinal % 10 == 2 then ordsuffix = i18n.ordinal[2] elseif cardinal % 10 == 3 then ordsuffix = i18n.ordinal[3] end -- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th' -- similarly for 12 and 13, etc. if (cardinal % 100 == 11) or (cardinal % 100 == 12) or (cardinal % 100 == 13) then ordsuffix = i18n.ordinal.default end return tostring(cardinal) .. ordsuffix end ------------------------------------------------------------------------------- -- findLang takes a "langcode" parameter if supplied and valid -- otherwise it tries to create it from the user's set language ({{int:lang}}) -- failing that it uses the wiki's content language. -- It returns a language object ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local findLang = function(langcode) local langobj langcode = mw.text.trim(langcode or "") if mw.language.isKnownLanguageTag(langcode) then langobj = mw.language.new( langcode ) else langcode = mw.getCurrentFrame():callParserFunction('int', {'lang'}) if mw.language.isKnownLanguageTag(langcode) then langobj = mw.language.new( langcode ) else langobj = mw.language.getContentLanguage() end end return langobj end ------------------------------------------------------------------------------- -- _getItemLangCode takes a qid parameter (using the current page's qid if blank) -- If the item for that qid has property country (P17) it looks at the first preferred value -- If the country has an official language (P37), it looks at the first preferred value -- If that official language has a language code (P424), it returns the first preferred value -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local _getItemLangCode = function(qid) qid = mw.text.trim(qid or ""):upper() if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return end local prop17 = mw.wikibase.getBestStatements(qid, "P17")[1] if not prop17 or prop17.mainsnak.snaktype ~= "value" then return end local qid17 = prop17.mainsnak.datavalue.value.id local prop37 = mw.wikibase.getBestStatements(qid17, "P37")[1] if not prop37 or prop37.mainsnak.snaktype ~= "value" then return end local qid37 = prop37.mainsnak.datavalue.value.id local prop424 = mw.wikibase.getBestStatements(qid37, "P424")[1] if not prop424 or prop424.mainsnak.snaktype ~= "value" then return end return prop424.mainsnak.datavalue.value end ------------------------------------------------------------------------------- -- roundto takes a number (x) -- and returns it rounded to (sf) significant figures ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local roundto = function(x, sf) if x == 0 then return 0 end local s = 1 if x < 0 then x = -x s = -1 end if sf < 1 then sf = 1 end local p = 10 ^ (math.floor(math.log10(x)) - sf + 1) x = math.floor(x / p + 0.5) * p * s -- if it's integral, cast to an integer: if x == math.floor(x) then x = math.floor(x) end return x end ------------------------------------------------------------------------------- -- decimalToDMS takes a decimal degrees (x) with precision (p) -- and returns degrees/minutes/seconds according to the precision ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local decimalToDMS = function(x, p) -- if p is not supplied, use a precision around 0.1 seconds if not tonumber(p) then p = 1e-4 end local d = math.floor(x) local ms = (x - d) * 60 if p > 0.5 then -- precision is > 1/2 a degree if ms > 30 then d = d + 1 end ms = 0 end local m = math.floor(ms) local s = (ms - m) * 60 if p > 0.008 then -- precision is > 1/2 a minute if s > 30 then m = m +1 end s = 0 elseif p > 0.00014 then -- precision is > 1/2 a second s = math.floor(s + 0.5) elseif p > 0.000014 then -- precision is > 1/20 second s = math.floor(10 * s + 0.5) / 10 elseif p > 0.0000014 then -- precision is > 1/200 second s = math.floor(100 * s + 0.5) / 100 else -- cap it at 3 dec places for now s = math.floor(1000 * s + 0.5) / 1000 end return d, m, s end ------------------------------------------------------------------------------- -- decimalPrecision takes a decimal (x) with precision (p) -- and returns x rounded approximately to the given precision -- precision should be between 1 and 1e-6, preferably a power of 10. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local decimalPrecision = function(x, p) local s = 1 if x < 0 then x = -x s = -1 end -- if p is not supplied, pick an arbitrary precision if not tonumber(p) then p = 1e-4 elseif p > 1 then p = 1 elseif p < 1e-6 then p = 1e-6 else p = 10 ^ math.floor(math.log10(p)) end x = math.floor(x / p + 0.5) * p * s -- if it's integral, cast to an integer: if x == math.floor(x) then x = math.floor(x) end -- if it's less than 1e-4, it will be in exponent form, so return a string with 6dp -- 9e-5 becomes 0.000090 if math.abs(x) < 1e-4 then x = string.format("%f", x) end return x end ------------------------------------------------------------------------------- -- formatDate takes a datetime of the usual format from mw.wikibase.entity:formatPropertyValues -- like "1 August 30 BCE" as parameter 1 -- and formats it according to the df (date format) and bc parameters -- df = ["dmy" / "mdy" / "y"] default will be "dmy" -- bc = ["BC" / "BCE"] default will be "BCE" ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local format_Date = function(datetime, dateformat, bc) local datetime = datetime or "1 August 30 BCE" -- in case of nil value -- chop off multiple vales and/or any hours, mins, etc. -- keep anything before punctuation - we just want a single date: local dateval = string.match( datetime, "[%w ]+") local dateformat = string.lower(dateformat or "dmy") -- default to dmy local bc = string.upper(bc or "") -- can't use nil for bc -- we only want to accept two possibilities: BC or default to BCE if bc == "BC" then bc = "&nbsp;" .. i18n["BC"] -- prepend a non-breaking space. else bc = "&nbsp;" .. i18n["BCE"] end local postchrist = true -- start by assuming no BCE local dateparts = {} for word in string.gmatch(dateval, "%w+") do if word == "BCE" or word == "BC" then -- *** internationalise later *** postchrist = false else -- we'll keep the parts that are not 'BCE' in a table dateparts[#dateparts + 1] = word end end if postchrist then bc = "" end -- set AD dates to no suffix *** internationalise later *** local sep = "&nbsp;" -- separator is nbsp local fdate = table.concat(dateparts, sep) -- set formatted date to same order as input -- if we have day month year, check dateformat if #dateparts == 3 then if dateformat == "y" then fdate = dateparts[3] elseif dateformat == "mdy" then fdate = dateparts[2] .. sep .. dateparts[1] .. "," .. sep .. dateparts[3] end elseif #dateparts == 2 and dateformat == "y" then fdate = dateparts[2] end return fdate .. bc end ------------------------------------------------------------------------------- -- dateFormat is the handler for properties that are of type "time" -- It takes timestamp, precision (6 to 11 per mediawiki), dateformat (y/dmy/mdy), BC format (BC/BCE), -- a plaindate switch (yes/no/adj) to en/disable "sourcing circumstances"/use adjectival form, -- any qualifiers for the property, the language, and any adjective to use like 'before'. -- It passes the date through the "complex date" function -- and returns a string with the internatonalised date formatted according to preferences. ------------------------------------------------------------------------------- -- Dependencies: findLang(); cdate(); dp[] ------------------------------------------------------------------------------- local dateFormat = function(timestamp, dprec, df, bcf, pd, qualifiers, lang, adj, model) -- output formatting according to preferences (y/dmy/mdy/ymd) df = (df or ""):lower() -- if ymd is required, return the part of the timestamp in YYYY-MM-DD form -- but apply Year zero#Astronomers fix: 1 BC = 0000; 2 BC = -0001; etc. if df == "ymd" then if timestamp:sub(1,1) == "+" then return timestamp:sub(2,11) else local yr = tonumber(timestamp:sub(2,5)) - 1 yr = ("000" .. yr):sub(-4) if yr ~= "0000" then yr = "-" .. yr end return yr .. timestamp:sub(6,11) end end -- A year can be stored like this: "+1872-00-00T00:00:00Z", -- which is processed here as if it were the day before "+1872-01-01T00:00:00Z", -- and that's the last day of 1871, so the year is wrong. -- So fix the month 0, day 0 timestamp to become 1 January instead: timestamp = timestamp:gsub("%-00%-00T", "-01-01T") -- just in case date precision is missing dprec = dprec or 11 -- override more precise dates if required dateformat is year alone: if df == "y" and dprec > 9 then dprec = 9 end -- complex date only deals with precisions from 6 to 11, so clip range dprec = dprec>11 and 11 or dprec dprec = dprec<6 and 6 or dprec -- BC format is "BC" or "BCE" bcf = (bcf or ""):upper() -- plaindate only needs the first letter (y/n/a) pd = (pd or ""):sub(1,1):lower() if pd == "" or pd == "n" or pd == "f" or pd == "0" then pd = false end -- in case language isn't passed lang = lang or findLang().code -- set adj as empty if nil adj = adj or "" -- extract the day, month, year from the timestamp local bc = timestamp:sub(1, 1)=="-" and "BC" or "" local year, month, day = timestamp:match("[+-](%d*)-(%d*)-(%d*)T") local iso = tonumber(year) -- if year is missing, let it throw an error -- this will adjust the date format to be compatible with cdate -- possible formats are Y, YY, YYY0, YYYY, YYYY-MM, YYYY-MM-DD if dprec == 6 then iso = math.floor( (iso - 1) / 1000 ) + 1 end if dprec == 7 then iso = math.floor( (iso - 1) / 100 ) + 1 end if dprec == 8 then iso = math.floor( iso / 10 ) .. "0" end if dprec == 10 then iso = year .. "-" .. month end if dprec == 11 then iso = year .. "-" .. month .. "-" .. day end -- add "circa" (Q5727902) from "sourcing circumstances" (P1480) local sc = not pd and qualifiers and qualifiers.P1480 if sc then for k1, v1 in pairs(sc) do if v1.datavalue and v1.datavalue.value.id == "Q5727902" then adj = "circa" break end end end -- deal with Julian dates: -- no point in saying that dates before 1582 are Julian - they are by default -- doesn't make sense for dates less precise than year -- we can suppress it by setting |plaindate, e.g. for use in constructing categories. local calendarmodel = "" if tonumber(year) > 1582 and dprec > 8 and not pd and model == "http://www.wikidata.org/entity/Q1985786" then calendarmodel = "julian" end if not cdate then cdate = require("Module:Complex date")._complex_date end local fdate = cdate(calendarmodel, adj, tostring(iso), dp[dprec], bc, "", "", "", "", lang, 1) -- this may have QuickStatements info appended to it in a div, so remove that fdate = fdate:gsub(' <div style="display: none;">[^<]*</div>', '') -- it may also be returned wrapped in a microformat, so remove that fdate = fdate:gsub("<[^>]*>", "") -- there may be leading zeros that we should remove fdate = fdate:gsub("^0*", "") -- if a plain date is required, then remove any links (like BC linked) if pd then fdate = fdate:gsub("%[%[.*|", ""):gsub("]]", "") end -- if 'circa', use the abbreviated form *** internationalise later *** fdate = fdate:gsub('circa ', '<abbr title="circa">c.</abbr>&nbsp;') -- deal with BC/BCE if bcf == "BCE" then fdate = fdate:gsub('BC', 'BCE') end -- deal with mdy format if df == "mdy" then fdate = fdate:gsub("(%d+) (%w+) (%d+)", "%2 %1, %3") end -- deal with adjectival form *** internationalise later *** if pd == "a" then fdate = fdate:gsub(' century', '-century') end return fdate end ------------------------------------------------------------------------------- -- parseParam takes a (string) parameter, e.g. from the list of frame arguments, -- and makes "false", "no", and "0" into the (boolean) false -- it makes the empty string and nil into the (boolean) value passed as default -- allowing the parameter to be true or false by default. -- It returns a boolean. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local parseParam = function(param, default) if type(param) == "boolean" then param = tostring(param) end if param and param ~= "" then param = param:lower() if (param == "false") or (param:sub(1,1) == "n") or (param == "0") then return false else return true end else return default end end ------------------------------------------------------------------------------- -- _getSitelink takes the qid of a Wikidata entity passed as |qid= -- It takes an optional parameter |wiki= to determine which wiki is to be checked for a sitelink -- If the parameter is blank, then it uses the local wiki. -- If there is a sitelink to an article available, it returns the plain text link to the article -- If there is no sitelink, it returns nil. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local _getSitelink = function(qid, wiki) qid = (qid or ""):upper() if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end wiki = wiki or "" local sitelink if wiki == "" then sitelink = mw.wikibase.getSitelink(qid) else sitelink = mw.wikibase.getSitelink(qid, wiki) end return sitelink end ------------------------------------------------------------------------------- -- _getCommonslink takes an optional qid of a Wikidata entity passed as |qid= -- It returns one of the following in order of preference: -- the Commons sitelink of the Wikidata entity - but not if onlycat=true and it's not a category; -- the Commons sitelink of the topic's main category of the Wikidata entity; -- the Commons category of the Wikidata entity - unless fallback=false. ------------------------------------------------------------------------------- -- Dependencies: _getSitelink(); parseParam() ------------------------------------------------------------------------------- local _getCommonslink = function(qid, onlycat, fallback) qid = (qid or ""):upper() if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end onlycat = parseParam(onlycat, false) if fallback == "" then fallback = nil end local sitelink = _getSitelink(qid, "commonswiki") if onlycat and sitelink and sitelink:sub(1,9) ~= "Category:" then sitelink = nil end if not sitelink then -- check for topic's main category local prop910 = mw.wikibase.getBestStatements(qid, "P910")[1] if prop910 then local tmcid = prop910.mainsnak.datavalue and prop910.mainsnak.datavalue.value.id sitelink = _getSitelink(tmcid, "commonswiki") end if not sitelink then -- check for list's main category local prop1754 = mw.wikibase.getBestStatements(qid, "P1754")[1] if prop1754 then local tmcid = prop1754.mainsnak.datavalue and prop1754.mainsnak.datavalue.value.id sitelink = _getSitelink(tmcid, "commonswiki") end end end if not sitelink and fallback then -- check for Commons category (string value) local prop373 = mw.wikibase.getBestStatements(qid, "P373")[1] if prop373 then sitelink = prop373.mainsnak.datavalue and prop373.mainsnak.datavalue.value if sitelink then sitelink = "Category:" .. sitelink end end end return sitelink end ------------------------------------------------------------------------------- -- The label in a Wikidata item is subject to vulnerabilities -- that an attacker might try to exploit. -- It needs to be 'sanitised' by removing any wikitext before use. -- If it doesn't exist, return the id for the item -- a second (boolean) value is also returned, value is true when the label exists ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local labelOrId = function(id, lang) if lang == "default" then lang = findLang().code end local label if lang then label = mw.wikibase.getLabelByLang(id, lang) else label = mw.wikibase.getLabel(id) end if label then return mw.text.nowiki(label), true else return id, false end end ------------------------------------------------------------------------------- -- linkedItem takes an entity-id and returns a string, linked if possible. -- This is the handler for "wikibase-item". Preferences: -- 1. Display linked disambiguated sitelink if it exists -- 2. Display linked label if it is a redirect -- 3. TBA: Display an inter-language link for the label if it exists other than in default language -- 4. Display unlinked label if it exists -- 5. Display entity-id for now to indicate a label could be provided -- dtxt is text to be used instead of label, or nil. -- shortname is boolean switch to use P1813 (short name) instead of label if true. -- lang is the current language code. -- uselbl is boolean switch to force display of the label instead of the sitelink (default: false) -- linkredir is boolean switch to allow linking to a redirect (default: false) -- formatvalue is boolean switch to allow formatting as italics or quoted (default: false) ------------------------------------------------------------------------------- -- Dependencies: labelOrId(); donotlink[] ------------------------------------------------------------------------------- local linkedItem = function(id, args) local lprefix = (args.lp or args.lprefix or args.linkprefix or ""):gsub('"', '') -- toughen against nil values passed local lpostfix = (args.lpostfix or ""):gsub('"', '') local prefix = (args.prefix or ""):gsub('"', '') local postfix = (args.postfix or ""):gsub('"', '') local dtxt = args.dtxt local shortname = args.shortname local lang = args.lang or "en" -- fallback to default if missing local uselbl = args.uselabel or args.uselbl uselbl = parseParam(uselbl, false) local linkredir = args.linkredir linkredir = parseParam(linkredir, false) local formatvalue = args.formatvalue or args.fv formatvalue = parseParam(formatvalue, false) -- see if item might need italics or quotes local fmt = "" if next(formats) and formatvalue then for k, v in ipairs( mw.wikibase.getBestStatements(id, "P31") ) do if v.mainsnak.datavalue and formats[v.mainsnak.datavalue.value.id] then fmt = formats[v.mainsnak.datavalue.value.id] break -- pick the first match end end end local disp local sitelink = mw.wikibase.getSitelink(id) local label, islabel if dtxt then label, islabel = dtxt, true elseif shortname then -- see if there is a shortname in our language, and set label to it for k, v in ipairs( mw.wikibase.getBestStatements(id, "P1813") ) do if v.mainsnak.datavalue.value.language == lang then label, islabel = v.mainsnak.datavalue.value.text, true break end -- test for language match end -- loop through values of short name -- if we have no label set, then there was no shortname available if not islabel then label, islabel = labelOrId(id) shortname = false end else label, islabel = labelOrId(id) end if mw.site.siteName ~= "Wikimedia Commons" then if sitelink then if not (dtxt or shortname) then -- if sitelink and label are the same except for case, no need to process further if sitelink:lower() ~= label:lower() then -- strip any namespace or dab from the sitelink local pos = sitelink:find(":") or 0 local slink = sitelink if pos > 0 then local pfx = sitelink:sub(1,pos-1) if mw.site.namespaces[pfx] then -- that prefix is a valid namespace, so remove it slink = sitelink:sub(pos+1) end end -- remove stuff after commas or inside parentheses - ie. dabs slink = slink:gsub("%s%(.+%)$", ""):gsub(",.+$", "") -- if uselbl is false, use sitelink instead of label if not uselbl then -- use slink as display, preserving label case - find("^%u") is true for 1st char uppercase if label:find("^%u") then label = slink:gsub("^(%l)", string.upper) else label = slink:gsub("^(%u)", string.lower) end end end end if donotlink[label] then disp = prefix .. fmt .. label .. fmt .. postfix else disp = "[[" .. lprefix .. sitelink .. lpostfix .. "|" .. prefix .. fmt .. label .. fmt .. postfix .. "]]" end elseif islabel then -- no sitelink, label exists, so check if a redirect with that title exists, if linkredir is true -- display plain label by default disp = prefix .. fmt .. label .. fmt .. postfix if linkredir then local artitle = mw.title.new(label, 0) -- only nil if label has invalid chars if not donotlink[label] and artitle and artitle.redirectTarget then -- there's a redirect with the same title as the label, so let's link to that disp = "[[".. lprefix .. label .. lpostfix .. "|" .. prefix .. fmt .. label .. fmt .. postfix .. "]]" end end -- test if article title exists as redirect on current Wiki else -- no sitelink and no label, so return whatever was returned from labelOrId for now -- add tracking category [[Category:Articles with missing Wikidata information]] -- for enwiki, just return the tracking category if mw.wikibase.getGlobalSiteId() == "enwiki" then disp = i18n.missinginfocat else disp = prefix .. label .. postfix .. i18n.missinginfocat end end else local ccat = mw.wikibase.getBestStatements(id, "P373")[1] if ccat and ccat.mainsnak.datavalue then ccat = ccat.mainsnak.datavalue.value disp = "[[" .. lprefix .. "Category:" .. ccat .. lpostfix .. "|" .. prefix .. label .. postfix .. "]]" elseif sitelink then -- this asumes that if a sitelink exists, then a label also exists disp = "[[" .. lprefix .. sitelink .. lpostfix .. "|" .. prefix .. label .. postfix .. "]]" else -- no sitelink and no Commons cat, so return label from labelOrId for now disp = prefix .. label .. postfix end end return disp end ------------------------------------------------------------------------------- -- sourced takes a table representing a statement that may or may not have references -- it looks for a reference sourced to something not containing the word "wikipedia" -- it returns a boolean = true if it finds a sourced reference. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local sourced = function(claim) if claim.references then for kr, vr in pairs(claim.references) do local ref = mw.wikibase.renderSnaks(vr.snaks) if not ref:find("Wiki") then return true end end end end ------------------------------------------------------------------------------- -- setRanks takes a flag (parameter passed) that requests the values to return -- "b[est]" returns preferred if available, otherwise normal -- "p[referred]" returns preferred -- "n[ormal]" returns normal -- "d[eprecated]" returns deprecated -- multiple values are allowed, e.g. "preferred normal" (which is the default) -- "best" will override the other flags, and set p and n ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local setRanks = function(rank) rank = (rank or ""):lower() -- if nothing passed, return preferred and normal -- if rank == "" then rank = "p n" end local ranks = {} for w in string.gmatch(rank, "%a+") do w = w:sub(1,1) if w == "b" or w == "p" or w == "n" or w == "d" then ranks[w] = true end end -- check if "best" is requested or no ranks requested; and if so, set preferred and normal if ranks.b or not next(ranks) then ranks.p = true ranks.n = true end return ranks end ------------------------------------------------------------------------------- -- parseInput processes the Q-id , the blacklist and the whitelist -- if an input parameter is supplied, it returns that and ends the call. -- it returns (1) either the qid or nil indicating whether or not the call should continue -- and (2) a table containing all of the statements for the propertyID and relevant Qid -- if "best" ranks are requested, it returns those instead of all non-deprecated ranks ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- local parseInput = function(frame, input_parm, property_id) -- There may be a local parameter supplied, if it's blank, set it to nil input_parm = mw.text.trim(input_parm or "") if input_parm == "" then input_parm = nil end -- return nil if Wikidata is not available if not mw.wikibase then return false, input_parm end local args = frame.args -- can take a named parameter |qid which is the Wikidata ID for the article. -- if it's not supplied, use the id for the current page local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end -- if there's no Wikidata item for the current page return nil if not qid then return false, input_parm end -- The blacklist is passed in named parameter |suppressfields local blacklist = args.suppressfields or args.spf or "" -- The whitelist is passed in named parameter |fetchwikidata local whitelist = args.fetchwikidata or args.fwd or "" if whitelist == "" then whitelist = "NONE" end -- The name of the field that this function is called from is passed in named parameter |name local fieldname = args.name or "" if blacklist ~= "" then -- The name is compulsory when blacklist is used, so return nil if it is not supplied if fieldname == "" then return false, nil end -- If this field is on the blacklist, then return nil if blacklist:find(fieldname) then return false, nil end end -- If we got this far then we're not on the blacklist -- The blacklist overrides any locally supplied parameter as well -- If a non-blank input parameter was supplied return it if input_parm then return false, input_parm end -- We can filter out non-valid properties if property_id:sub(1,1):upper() ~="P" or property_id == "P0" then return false, nil end -- Otherwise see if this field is on the whitelist: -- needs a bit more logic because find will return its second value = 0 if fieldname is "" -- but nil if fieldname not found on whitelist local _, found = whitelist:find(fieldname) found = ((found or 0) > 0) if whitelist ~= 'ALL' and (whitelist:upper() == "NONE" or not found) then return false, nil end -- See what's on Wikidata (the call always returns a table, but it may be empty): local props = {} if args.reqranks.b then props = mw.wikibase.getBestStatements(qid, property_id) else props = mw.wikibase.getAllStatements(qid, property_id) end if props[1] then return qid, props end -- no property on Wikidata return false, nil end ------------------------------------------------------------------------------- -- createicon assembles the "Edit at Wikidata" pen icon. -- It returns a wikitext string inside a span class="penicon" -- if entityID is nil or empty, the ID associated with current page is used -- langcode and propertyID may be nil or empty ------------------------------------------------------------------------------- -- Dependencies: i18n[]; ------------------------------------------------------------------------------- local createicon = function(langcode, entityID, propertyID) langcode = langcode or "" if not entityID or entityID == "" then entityID= mw.wikibase.getEntityIdForCurrentPage() end propertyID = propertyID or "" local icon = "&nbsp;<span class='penicon autoconfirmed-show'>[[" -- "&nbsp;<span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge .. i18n["filespace"] .. ":OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt=" .. i18n["editonwikidata"] .. "|link=https://www.wikidata.org/wiki/" .. entityID if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end if propertyID ~= "" then icon = icon .. "#" .. propertyID end icon = icon .. "|" .. i18n["editonwikidata"] .. "]]</span>" return icon end ------------------------------------------------------------------------------- -- assembleoutput takes the sequence table containing the property values -- and formats it according to switches given. It returns a string or nil. -- It uses the entityID (and optionally propertyID) to create a link in the pen icon. ------------------------------------------------------------------------------- -- Dependencies: parseParam(); ------------------------------------------------------------------------------- local assembleoutput = function(out, args, entityID, propertyID) -- sorted is a boolean passed to enable sorting of the values returned -- if nothing or an empty string is passed set it false -- if "false" or "no" or "0" is passed set it false local sorted = parseParam(args.sorted, false) -- noicon is a boolean passed to suppress the trailing "edit at Wikidata" icon -- for use when the value is processed further by the infobox -- if nothing or an empty string is passed set it false -- if "false" or "no" or "0" is passed set it false local noic = parseParam(args.noicon, false) -- list is the name of a template that a list of multiple values is passed through -- examples include "hlist" and "ubl" -- setting it to "prose" produces something like "1, 2, 3, and 4" local list = args.list or "" -- sep is a string that is used to separate multiple returned values -- if nothing or an empty string is passed set it to the default -- any double-quotes " are stripped out, so that spaces may be passed -- e.g. |sep=" - " local sepdefault = i18n["list separator"] local separator = args.sep or "" separator = string.gsub(separator, '"', '') if separator == "" then separator = sepdefault end -- collapse is a number that determines the maximum number of returned values -- before the output is collapsed. -- Zero or not a number result in no collapsing (default becomes 0). local collapse = tonumber(args.collapse) or 0 -- replacetext (rt) is a string that is returned instead of any non-empty Wikidata value -- this is useful for tracking and debugging local replacetext = mw.text.trim(args.rt or args.replacetext or "") -- if there's anything to return, then return a list -- comma-separated by default, but may be specified by the sep parameter -- optionally specify a hlist or ubl or a prose list, etc. local strout if #out > 0 then if sorted then table.sort(out) end -- if there's something to display and a pen icon is wanted, add it the end of the last value local hasdisplay = false for i, v in ipairs(out) do if v ~= i18n.missinginfocat then hasdisplay = true break end end if not noic and hasdisplay then out[#out] = out[#out] .. createicon(args.langobj.code, entityID, propertyID) end if list == "" then strout = table.concat(out, separator) elseif list:lower() == "prose" then strout = mw.text.listToText( out ) else strout = mw.getCurrentFrame():expandTemplate{title = list, args = out} end if collapse >0 and #out > collapse then strout = collapsediv .. strout .. "</div>" end else strout = nil -- no items had valid reference end if replacetext ~= "" and strout then strout = replacetext end return strout end ------------------------------------------------------------------------------- -- rendersnak takes a table (propval) containing the information stored on one property value -- and returns the value as a string and its language if monolingual text. -- It handles data of type: -- wikibase-item -- time -- string, url, commonsMedia, external-id -- quantity -- globe-coordinate -- monolingualtext -- It also requires linked, the link/pre/postfixes, uabbr, and the arguments passed from frame. -- The optional filter parameter allows quantities to be be filtered by unit Qid. ------------------------------------------------------------------------------- -- Dependencies: parseParam(); labelOrId(); i18n[]; dateFormat(); -- roundto(); decimalPrecision(); decimalToDMS(); linkedItem(); ------------------------------------------------------------------------------- local rendersnak = function(propval, args, linked, lpre, lpost, pre, post, uabbr, filter) lpre = lpre or "" lpost = lpost or "" pre = pre or "" post = post or "" args.lang = args.lang or findLang().code -- allow values to display a fixed text instead of label local dtxt = args.displaytext or args.dt if dtxt == "" then dtxt = nil end -- switch to use display of short name (P1813) instead of label local shortname = args.shortname or args.sn shortname = parseParam(shortname, false) local snak = propval.mainsnak or propval local dtype = snak.datatype local dv = snak.datavalue dv = dv and dv.value -- value and monolingual text language code returned local val, mlt if propval.rank and not args.reqranks[propval.rank:sub(1, 1)] then -- val is nil: value has a rank that isn't requested ------------------------------------ elseif snak.snaktype == "somevalue" then -- value is unknown val = i18n["Unknown"] ------------------------------------ elseif snak.snaktype == "novalue" then -- value is none -- val = "No value" -- don't return anything ------------------------------------ elseif dtype == "wikibase-item" then -- data type is a wikibase item: -- it's wiki-linked value, so output as link if enabled and possible local qnumber = dv.id if linked then val = linkedItem(qnumber, args) else -- no link wanted so check for display-text, otherwise test for lang code local label, islabel if dtxt then label = dtxt else label, islabel = labelOrId(qnumber) local langlabel = mw.wikibase.getLabelByLang(qnumber, args.lang) if langlabel then label = mw.text.nowiki( langlabel ) end end val = pre .. label .. post end -- test for link required ------------------------------------ elseif dtype == "time" then -- data type is time: -- time is in timestamp format -- date precision is integer per mediawiki -- output formatting according to preferences (y/dmy/mdy) -- BC format as BC or BCE -- plaindate is passed to disable looking for "sourcing cirumstances" -- or to set the adjectival form -- qualifiers (if any) is a nested table or nil -- lang is given, or user language, or site language -- -- Here we can check whether args.df has a value -- If not, use code from Module:Sandbox/RexxS/Getdateformat to set it from templates like {{Use mdy dates}} val = dateFormat(dv.time, dv.precision, args.df, args.bc, args.pd, propval.qualifiers, args.lang, "", dv.calendarmodel) ------------------------------------ -- data types which are strings: elseif dtype == "commonsMedia" or dtype == "external-id" or dtype == "string" or dtype == "url" then -- commonsMedia or external-id or string or url -- all have mainsnak.datavalue.value as string if (lpre == "" or lpre == ":") and lpost == "" then -- don't link if no linkpre/postfix or linkprefix is just ":" val = pre .. dv .. post elseif dtype == "external-id" then val = "[" .. lpre .. dv .. lpost .. " " .. pre .. dv .. post .. "]" else val = "[[" .. lpre .. dv .. lpost .. "|" .. pre .. dv .. post .. "]]" end -- check for link requested (i.e. either linkprefix or linkpostfix exists) ------------------------------------ -- data types which are quantities: elseif dtype == "quantity" then -- quantities have mainsnak.datavalue.value.amount and mainsnak.datavalue.value.unit -- the unit is of the form http://www.wikidata.org/entity/Q829073 -- -- implement a switch to turn on/off numerical formatting later local fnum = true -- -- a switch to turn on/off conversions - only for en-wiki local conv = parseParam(args.conv or args.convert, false) -- if we have conversions, we won't have formatted numbers or scales if conv then uabbr = true fnum = false args.scale = "0" end -- -- a switch to turn on/off showing units, default is true local showunits = parseParam(args.su or args.showunits, true) -- -- convert amount to a number local amount = tonumber(dv.amount) or i18n["NaN"] -- -- scale factor for millions, billions, etc. local sc = tostring(args.scale or ""):sub(1,1):lower() local scale if sc == "a" then -- automatic scaling if amount > 1e15 then scale = 12 elseif amount > 1e12 then scale = 9 elseif amount > 1e9 then scale = 6 elseif amount > 1e6 then scale = 3 else scale = 0 end else scale = tonumber(args.scale) or 0 if scale < 0 or scale > 12 then scale = 0 end scale = math.floor(scale/3) * 3 end local factor = 10^scale amount = amount / factor -- ranges: local range = "" -- check if upper and/or lower bounds are given and significant local upb = tonumber(dv.upperBound) local lowb = tonumber(dv.lowerBound) if upb and lowb then -- differences rounded to 2 sig fig: local posdif = roundto(upb - amount, 2) / factor local negdif = roundto(amount - lowb, 2) / factor upb, lowb = amount + posdif, amount - negdif -- round scaled numbers to integers or 4 sig fig if (scale > 0 or sc == "a") then if amount < 1e4 then amount = roundto(amount, 4) else amount = math.floor(amount + 0.5) end end if fnum then amount = args.langobj:formatNum( amount ) end if posdif ~= negdif then -- non-symmetrical range = " +" .. posdif .. " -" .. negdif elseif posdif ~= 0 then -- symmetrical and non-zero range = " ±" .. posdif else -- otherwise range is zero, so leave it as "" end else -- round scaled numbers to integers or 4 sig fig if (scale > 0 or sc == "a") then if amount < 1e4 then amount = roundto(amount, 4) else amount = math.floor(amount + 0.5) end end if fnum then amount = args.langobj:formatNum( amount ) end end -- unit names and symbols: -- extract the qid in the form 'Qnnn' from the value.unit url -- and then fetch the label from that - or symbol if unitabbr is true local unit = "" local usep = "" local usym = "" local unitqid = string.match( dv.unit, "(Q%d+)" ) if filter and unitqid ~= filter then return nil end if unitqid and showunits then local uname = mw.wikibase.getLabelByLang(unitqid, args.lang) or "" if uname ~= "" then usep, unit = " ", uname end if uabbr then -- see if there's a unit symbol (P5061) local unitsymbols = mw.wikibase.getBestStatements(unitqid, "P5061") -- construct fallback table, add local lang and multiple languages local fbtbl = mw.language.getFallbacksFor( args.lang ) table.insert( fbtbl, 1, args.lang ) table.insert( fbtbl, 1, "mul" ) local found = false for idx1, us in ipairs(unitsymbols) do for idx2, fblang in ipairs(fbtbl) do if us.mainsnak.datavalue.value.language == fblang then usym = us.mainsnak.datavalue.value.text found = true break end if found then break end end -- loop through fallback table end -- loop through values of P5061 if found then usep, unit = "&nbsp;", usym end end end -- format display: if conv then if range == "" then val = mw.getCurrentFrame():expandTemplate{title = "cvt", args = {amount, unit}} else val = mw.getCurrentFrame():expandTemplate{title = "cvt", args = {lowb, "to", upb, unit}} end elseif unit == "$" or unit == "£" then val = unit .. amount .. range .. i18n.multipliers[scale] else val = amount .. range .. i18n.multipliers[scale] .. usep .. unit end ------------------------------------ -- datatypes which are global coordinates: elseif dtype == "globe-coordinate" then -- 'display' parameter defaults to "inline, title" *** unused for now *** -- local disp = args.display or "" -- if disp == "" then disp = "inline, title" end -- -- format parameter switches from deg/min/sec to decimal degrees -- default is deg/min/sec -- decimal degrees needs |format = dec local form = (args.format or ""):lower():sub(1,3) if form ~= "dec" then form = "dms" end -- not needed for now -- -- show parameter allows just the latitude, or just the longitude, or both -- to be returned as a signed decimal, ignoring the format parameter. local show = (args.show or ""):lower() if show ~= "longlat" then show = show:sub(1,3) end -- local lat, long, prec = dv.latitude, dv.longitude, dv.precision if show == "lat" then val = decimalPrecision(lat, prec) elseif show == "lon" then val = decimalPrecision(long, prec) elseif show == "longlat" then val = decimalPrecision(long, prec) .. ", " .. decimalPrecision(lat, prec) else local ns = "N" local ew = "E" if lat < 0 then ns = "S" lat = - lat end if long < 0 then ew = "W" long = - long end if form == "dec" then lat = decimalPrecision(lat, prec) long = decimalPrecision(long, prec) val = lat .. "°" .. ns .. " " .. long .. "°" .. ew else local latdeg, latmin, latsec = decimalToDMS(lat, prec) local longdeg, longmin, longsec = decimalToDMS(long, prec) if latsec == 0 and longsec == 0 then if latmin == 0 and longmin == 0 then val = latdeg .. "°" .. ns .. " " .. longdeg .. "°" .. ew else val = latdeg .. "°" .. latmin .. "′" .. ns .. " " val = val .. longdeg .. "°".. longmin .. "′" .. ew end else val = latdeg .. "°" .. latmin .. "′" .. latsec .. "″" .. ns .. " " val = val .. longdeg .. "°" .. longmin .. "′" .. longsec .. "″" .. ew end end end ------------------------------------ elseif dtype == "monolingualtext" then -- data type is Monolingual text: -- has mainsnak.datavalue.value as a table containing language/text pairs -- collect all the values in 'out' and languages in 'mlt' and process them later val = pre .. dv.text .. post mlt = dv.language ------------------------------------ else -- some other data type so write a specific handler val = "unknown data type: " .. dtype end -- of datatype/unknown value/sourced check return val, mlt end ------------------------------------------------------------------------------- -- propertyvalueandquals takes a property object, the arguments passed from frame, -- and a qualifier propertyID. -- It returns a sequence (table) of values representing the values of that property -- and qualifiers that match the qualifierID if supplied. ------------------------------------------------------------------------------- -- Dependencies: parseParam(); sourced(); labelOrId(); i18n.latestdatequalifier(); format_Date(); -- makeOrdinal(); roundto(); decimalPrecision(); decimalToDMS(); assembleoutput(); ------------------------------------------------------------------------------- local function propertyvalueandquals(objproperty, args, qualID) -- needs this style of declaration because it's re-entrant -- onlysourced is a boolean passed to return only values sourced to other than Wikipedia -- if nothing or an empty string is passed set it true local onlysrc = parseParam(args.onlysourced or args.osd, true) -- linked is a a boolean that enables the link to a local page via sitelink -- if nothing or an empty string is passed set it true local linked = parseParam(args.linked, true) -- prefix is a string that may be nil, empty (""), or a string of characters -- this is prefixed to each value -- useful when when multiple values are returned -- any double-quotes " are stripped out, so that spaces may be passed local prefix = (args.prefix or ""):gsub('"', '') -- postfix is a string that may be nil, empty (""), or a string of characters -- this is postfixed to each value -- useful when when multiple values are returned -- any double-quotes " are stripped out, so that spaces may be passed local postfix = (args.postfix or ""):gsub('"', '') -- linkprefix is a string that may be nil, empty (""), or a string of characters -- this creates a link and is then prefixed to each value -- useful when when multiple values are returned and indirect links are needed -- any double-quotes " are stripped out, so that spaces may be passed local lprefix = (args.linkprefix or args.lp or ""):gsub('"', '') -- linkpostfix is a string that may be nil, empty (""), or a string of characters -- this is postfixed to each value when linking is enabled with lprefix -- useful when when multiple values are returned -- any double-quotes " are stripped out, so that spaces may be passed local lpostfix = (args.linkpostfix or ""):gsub('"', '') -- wdlinks is a boolean passed to enable links to Wikidata when no article exists -- if nothing or an empty string is passed set it false local wdl = parseParam(args.wdlinks or args.wdl, false) -- unitabbr is a boolean passed to enable unit abbreviations for common units -- if nothing or an empty string is passed set it false local uabbr = parseParam(args.unitabbr or args.uabbr, false) -- qualsonly is a boolean passed to return just the qualifiers -- if nothing or an empty string is passed set it false local qualsonly = parseParam(args.qualsonly or args.qo, false) -- maxvals is a string that may be nil, empty (""), or a number -- this determines how many items may be returned when multiple values are available -- setting it = 1 is useful where the returned string is used within another call, e.g. image local maxvals = tonumber(args.maxvals) or 0 -- pd (plain date) is a string: yes/true/1 | no/false/0 | adj -- to disable/enable "sourcing cirumstances" or use adjectival form for the plain date local pd = args.plaindate or args.pd or "no" args.pd = pd -- allow qualifiers to have a different date format; default to year unless qualsonly is set args.qdf = args.qdf or args.qualifierdateformat or args.df or (not qualsonly and "y") local lang = args.lang or findLang().code -- qualID is a string list of wanted qualifiers or "ALL" qualID = qualID or "" -- capitalise list of wanted qualifiers and substitute "DATES" qualID = qualID:upper():gsub("DATES", "P580, P582") local allflag = (qualID == "ALL") -- create table of wanted qualifiers as key local qwanted = {} -- create sequence of wanted qualifiers local qorder = {} for q in mw.text.gsplit(qualID, "%p") do -- split at punctuation and iterate local qtrim = mw.text.trim(q) if qtrim ~= "" then qwanted[mw.text.trim(q)] = true qorder[#qorder+1] = qtrim end end -- qsep is the output separator for rendering qualifier list local qsep = (args.qsep or ""):gsub('"', '') -- qargs are the arguments to supply to assembleoutput() local qargs = { ["osd"] = "false", ["linked"] = tostring(linked), ["prefix"] = args.qprefix, ["postfix"] = args.qpostfix, ["linkprefix"] = args.qlinkprefix or args.qlp, ["linkpostfix"] = args.qlinkpostfix, ["wdl"] = "false", ["unitabbr"] = tostring(uabbr), ["maxvals"] = 0, ["sorted"] = tostring(args.qsorted), ["noicon"] = "true", ["list"] = args.qlist, ["sep"] = qsep, ["langobj"] = args.langobj, ["lang"] = args.langobj.code, ["df"] = args.qdf, ["sn"] = parseParam(args.qsn or args.qshortname, false), } -- all proper values of a Wikidata property will be the same type as the first -- qualifiers don't have a mainsnak, properties do local datatype = objproperty[1].datatype or objproperty[1].mainsnak.datatype -- out[] holds the a list of returned values for this property -- mlt[] holds the language code if the datatype is monolingual text local out = {} local mlt = {} for k, v in ipairs(objproperty) do local hasvalue = true if (onlysrc and not sourced(v)) then -- no value: it isn't sourced when onlysourced=true hasvalue = false else local val, lcode = rendersnak(v, args, linked, lprefix, lpostfix, prefix, postfix, uabbr) if not val then hasvalue = false -- rank doesn't match elseif qualsonly and qualID then -- suppress value returned: only qualifiers are requested else out[#out+1], mlt[#out+1] = val, lcode end end -- See if qualifiers are to be returned: local snak = v.mainsnak or v if hasvalue and v.qualifiers and qualID ~= "" and snak.snaktype~="novalue" then -- collect all wanted qualifier values returned in qlist, indexed by propertyID local qlist = {} local timestart, timeend = "", "" -- loop through qualifiers for k1, v1 in pairs(v.qualifiers) do if allflag or qwanted[k1] then if k1 == "P1326" then local ts = v1[1].datavalue.value.time local dp = v1[1].datavalue.value.precision qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "before") elseif k1 == "P1319" then local ts = v1[1].datavalue.value.time local dp = v1[1].datavalue.value.precision qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "after") elseif k1 == "P580" then timestart = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one start time as valid elseif k1 == "P582" then timeend = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one end time as valid else local q = assembleoutput(propertyvalueandquals(v1, qargs), qargs) -- we already deal with circa via 'sourcing circumstances' if the datatype was time -- circa may be either linked or unlinked *** internationalise later *** if datatype ~= "time" or q ~= "circa" and not (type(q) == "string" and q:find("circa]]")) then qlist[k1] = q end end end -- of test for wanted end -- of loop through qualifiers -- set date separator local t = timestart .. timeend -- *** internationalise date separators later *** local dsep = "&ndash;" if t:find("%s") or t:find("&nbsp;") then dsep = " &ndash; " end -- set the order for the list of qualifiers returned; start time and end time go last if next(qlist) then local qlistout = {} if allflag then for k2, v2 in pairs(qlist) do qlistout[#qlistout+1] = v2 end else for i2, v2 in ipairs(qorder) do qlistout[#qlistout+1] = qlist[v2] end end if t ~= "" then qlistout[#qlistout+1] = timestart .. dsep .. timeend end local qstr = assembleoutput(qlistout, qargs) if qualsonly then out[#out+1] = qstr else out[#out] = out[#out] .. " (" .. qstr .. ")" end elseif t ~= "" then if qualsonly then if timestart == "" then out[#out+1] = timeend elseif timeend == "" then out[#out+1] = timestart else out[#out+1] = timestart .. dsep .. timeend end else out[#out] = out[#out] .. " (" .. timestart .. dsep .. timeend .. ")" end end end -- of test for qualifiers wanted if maxvals > 0 and #out >= maxvals then break end end -- of for each value loop -- we need to pick one value to return if the datatype was "monolingualtext" -- if there's only one value, use that -- otherwise look through the fallback languages for a match if datatype == "monolingualtext" and #out >1 then lang = mw.text.split( lang, '-', true )[1] local fbtbl = mw.language.getFallbacksFor( lang ) table.insert( fbtbl, 1, lang ) local bestval = "" local found = false for idx1, lang1 in ipairs(fbtbl) do for idx2, lang2 in ipairs(mlt) do if (lang1 == lang2) and not found then bestval = out[idx2] found = true break end end -- loop through values of property end -- loop through fallback languages if found then -- replace output table with a table containing the best value out = { bestval } else -- more than one value and none of them on the list of fallback languages -- sod it, just give them the first one out = { out[1] } end end return out end ------------------------------------------------------------------------------- -- Common code for p.getValueByQual and p.getValueByLang ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; assembleoutput; ------------------------------------------------------------------------------- local _getvaluebyqual = function(frame, qualID, checkvalue) -- The property ID that will have a qualifier is the first unnamed parameter local propertyID = mw.text.trim(frame.args[1] or "") if propertyID == "" then return "no property supplied" end if qualID == "" then return "no qualifier supplied" end -- onlysourced is a boolean passed to return property values -- only when property values are sourced to something other than Wikipedia -- if nothing or an empty string is passed set it true -- if "false" or "no" or 0 is passed set it false local onlysrc = parseParam(frame.args.onlysourced or frame.args.osd, true) -- set the requested ranks flags frame.args.reqranks = setRanks(frame.args.rank) -- set a language object and code in the frame.args table frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local args = frame.args -- check for locally supplied parameter in second unnamed parameter -- success means no local parameter and the property exists local qid, props = parseInput(frame, args[2], propertyID) local linked = parseParam(args.linked, true) local lpre = (args.linkprefix or args.lp or ""):gsub('"', '') local lpost = (args.linkpostfix or ""):gsub('"', '') local pre = (args.prefix or ""):gsub('"', '') local post = (args.postfix or ""):gsub('"', '') local uabbr = parseParam(args.unitabbr or args.uabbr, false) local filter = (args.unit or ""):upper() local maxvals = tonumber(args.maxvals) or 0 if filter == "" then filter = nil end if qid then local out = {} -- Scan through the values of the property -- we want something like property is "pronunciation audio (P443)" in propertyID -- with a qualifier like "language of work or name (P407)" in qualID -- whose value has the required ID, like "British English (Q7979)", in qval for k1, v1 in ipairs(props) do if v1.mainsnak.snaktype == "value" then -- check if it has the right qualifier local v1q = v1.qualifiers if v1q and v1q[qualID] then if onlysrc == false or sourced(v1) then -- if we've got this far, we have a (sourced) claim with qualifiers -- so see if matches the required value -- We'll only deal with wikibase-items and strings for now if v1q[qualID][1].datatype == "wikibase-item" then if checkvalue(v1q[qualID][1].datavalue.value.id) then out[#out + 1] = rendersnak(v1, args, linked, lpre, lpost, pre, post, uabbr, filter) end elseif v1q[qualID][1].datatype == "string" then if checkvalue(v1q[qualID][1].datavalue.value) then out[#out + 1] = rendersnak(v1, args, linked, lpre, lpost, pre, post, uabbr, filter) end end end -- of check for sourced end -- of check for matching required value and has qualifiers else return nil end -- of check for string if maxvals > 0 and #out >= maxvals then break end end -- of loop through values of propertyID return assembleoutput(out, frame.args, qid, propertyID) else return props -- either local parameter or nothing end -- of test for success return nil end ------------------------------------------------------------------------------- -- _location takes Q-id and follows P276 (location) -- or P131 (located in the administrative territorial entity) or P706 (located on terrain feature) -- from the initial item to higher level territories/locations until it reaches the highest. -- An optional boolean, 'first', determines whether the first item is returned (default: false). -- An optional boolean 'skip' toggles the display to skip to the last item (default: false). -- It returns a table containing the locations - linked where possible, except for the highest. ------------------------------------------------------------------------------- -- Dependencies: findLang(); labelOrId(); linkedItem ------------------------------------------------------------------------------- local _location = function(qid, first, skip) first = parseParam(first, false) skip = parseParam(skip, false) local locs = {"P276", "P131", "P706"} local out = {} local langcode = findLang():getCode() local finished = false local count = 0 local prevqid = "Q0" repeat local prop for i1, v1 in ipairs(locs) do local proptbl = mw.wikibase.getBestStatements(qid, v1) if #proptbl > 1 then -- there is more than one higher location local prevP131, prevP131id if prevqid ~= "Q0" then prevP131 = mw.wikibase.getBestStatements(prevqid, "P131")[1] prevP131id = prevP131 and prevP131.mainsnak.datavalue and prevP131.mainsnak.datavalue.value.id end for i2, v2 in ipairs(proptbl) do local parttbl = v2.qualifiers and v2.qualifiers.P518 if parttbl then -- this higher location has qualifier 'applies to part' (P518) for i3, v3 in ipairs(parttbl) do if v3.snaktype == "value" and v3.datavalue.value.id == prevqid then -- it has a value equal to the previous location prop = proptbl[i2] break end -- of test for matching last location end -- of loop through values of 'applies to part' else -- there's no qualifier 'applies to part' (P518) -- so check if the previous location had a P131 that matches this alternate if qid == prevP131id then prop = proptbl[i2] break end -- of test for matching previous P131 end end -- of loop through parent locations -- fallback to second value if match not found prop = prop or proptbl[2] elseif #proptbl > 0 then prop = proptbl[1] end if prop then break end end -- check if it's an instance of (P31) a country (Q6256) or sovereign state (Q3624078) -- and terminate the chain if it is local inst = mw.wikibase.getAllStatements(qid, "P31") if #inst > 0 then for k, v in ipairs(inst) do local instid = v.mainsnak.datavalue and v.mainsnak.datavalue.value.id -- stop if it's a country (or a country within the United Kingdom if skip is true) if instid == "Q6256" or instid == "Q3624078" or (skip and instid == "Q3336843") then prop = nil -- this will ensure this is treated as top-level location break end end end -- get the name of this location and update qid to point to the parent location if prop and prop.mainsnak.datavalue then if not skip or count == 0 then local args = { lprefix = ":" } out[#out+1] = linkedItem(qid, args) -- get a linked value if we can end qid, prevqid = prop.mainsnak.datavalue.value.id, qid else -- This is top-level location, so get short name except when this is the first item -- Use full label if there's no short name or this is the first item local prop1813 = mw.wikibase.getAllStatements(qid, "P1813") -- if there's a short name and this isn't the only item if prop1813[1] and (#out > 0)then local shortname -- short name is monolingual text, so look for match to the local language -- choose the shortest 'short name' in that language for k, v in pairs(prop1813) do if v.mainsnak.datavalue.value.language == langcode then local name = v.mainsnak.datavalue.value.text if (not shortname) or (#name < #shortname) then shortname = name end end end -- add the shortname if one is found, fallback to the label -- but skip it if it's "USA" if shortname ~= "USA" then out[#out+1] = shortname or labelOrId(qid) else if skip then out[#out+1] = "US" end end else -- no shortname, so just add the label local loc = labelOrId(qid) -- exceptions go here: if loc == "United States of America" then out[#out+1] = "United States" else out[#out+1] = loc end end finished = true end count = count + 1 until finished or count >= 10 -- limit to 10 levels to avoid infinite loops -- remove the first location if not required if not first then table.remove(out, 1) end -- we might have duplicate text for consecutive locations, so remove them if #out > 2 then local plain = {} for i, v in ipairs(out) do -- strip any links plain[i] = v:gsub("^%[%[[^|]*|", ""):gsub("]]$", "") end local idx = 2 repeat if plain[idx] == plain[idx-1] then -- duplicate found local removeidx = 0 if (plain[idx] ~= out[idx]) and (plain[idx-1] == out[idx-1]) then -- only second one is linked, so drop the first removeidx = idx - 1 elseif (plain[idx] == out[idx]) and (plain[idx-1] ~= out[idx-1]) then -- only first one is linked, so drop the second removeidx = idx else -- pick one removeidx = idx - (os.time()%2) end table.remove(out, removeidx) table.remove(plain, removeidx) else idx = idx +1 end until idx >= #out end return out end ------------------------------------------------------------------------------- -- _getsumofparts scans the property 'has part' (P527) for values matching a list. -- The list (args.vlist) consists of a string of Qids separated by spaces or any usual punctuation. -- If the matched values have a qualifer 'quantity' (P1114), those quantites are summed. -- The sum is returned as a number (i.e. 0 if none) -- a table of arguments is supplied implementing the usual parameters. ------------------------------------------------------------------------------- -- Dependencies: setRanks; parseParam; parseInput; sourced; assembleoutput; ------------------------------------------------------------------------------- local _getsumofparts = function(args) local vallist = (args.vlist or ""):upper() if vallist == "" then return end args.reqranks = setRanks(args.rank) local f = {} f.args = args local qid, props = parseInput(f, "", "P527") if not qid then return 0 end local onlysrc = parseParam(args.onlysourced or args.osd, true) local sum = 0 for k1, v1 in ipairs(props) do if (onlysrc == false or sourced(v1)) and v1.mainsnak.snaktype == "value" and v1.mainsnak.datavalue.type == "wikibase-entityid" and vallist:match( v1.mainsnak.datavalue.value.id ) and v1.qualifiers then local quals = v1.qualifiers["P1114"] if quals then for k2, v2 in ipairs(quals) do sum = sum + v2.datavalue.value.amount end end end end return sum end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- Public functions ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- _getValue makes the functionality of getValue available to other modules ------------------------------------------------------------------------------- -- Dependencies: setRanks; parseInput; propertyvalueandquals; assembleoutput; parseParam; sourced; -- labelOrId; i18n.latestdatequalifier; format_Date; makeOrdinal; roundto; decimalPrecision; decimalToDMS; ------------------------------------------------------------------------------- p._getValue = function(args) -- parameter sets for commonly used groups of parameters local paraset = tonumber(args.ps or args.parameterset or 0) if paraset == 1 then -- a common setting args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" elseif paraset == 2 then -- equivalent to raw args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" args.linked = "no" args.pd = "true" elseif paraset == 3 then -- third set goes here end -- implement eid parameter local eid = args.eid if eid == "" then return nil elseif eid then args.qid = eid end local propertyID = mw.text.trim(args[1] or "") args.reqranks = setRanks(args.rank) -- replacetext (rt) is a string that is returned instead of any non-empty Wikidata value -- this is useful for tracking and debugging, so we set fetchwikidata=ALL to fill the whitelist local replacetext = mw.text.trim(args.rt or args.replacetext or "") if replacetext ~= "" then args.fetchwikidata = "ALL" end local f = {} f.args = args local entityid, props = parseInput(f, f.args[2], propertyID) if not entityid then return props -- either the input parameter or nothing end -- qual is a string containing the property ID of the qualifier(s) to be returned -- if qual == "ALL" then all qualifiers returned -- if qual == "DATES" then qualifiers P580 (start time) and P582 (end time) returned -- if nothing or an empty string is passed set it nil -> no qualifiers returned local qualID = mw.text.trim(args.qual or ""):upper() if qualID == "" then qualID = nil end -- set a language object and code in the args table args.langobj = findLang(args.lang) args.lang = args.langobj.code -- table 'out' stores the return value(s): local out = propertyvalueandquals(props, args, qualID) -- format the table of values and return it as a string: return assembleoutput(out, args, entityid, propertyID) end ------------------------------------------------------------------------------- -- getValue is used to get the value(s) of a property -- The property ID is passed as the first unnamed parameter and is required. -- A locally supplied parameter may optionaly be supplied as the second unnamed parameter. -- The function will now also return qualifiers if parameter qual is supplied ------------------------------------------------------------------------------- -- Dependencies: _getValue; setRanks; parseInput; propertyvalueandquals; assembleoutput; parseParam; sourced; -- labelOrId; i18n.latestdatequalifier; format_Date; makeOrdinal; roundto; decimalPrecision; decimalToDMS; ------------------------------------------------------------------------------- p.getValue = function(frame) local args= frame.args if not args[1] then args = frame:getParent().args if not args[1] then return i18n.errors["No property supplied"] end end return p._getValue(args) end ------------------------------------------------------------------------------- -- getPreferredValue is used to get a value, -- (or a comma separated list of them if multiple values exist). -- If preferred ranks are set, it will return those values, otherwise values with normal ranks -- now redundant to getValue with |rank=best ------------------------------------------------------------------------------- -- Dependencies: p.getValue; setRanks; parseInput; propertyvalueandquals; assembleoutput; -- parseParam; sourced; labelOrId; i18n.latestdatequalifier; format_Date; -- makeOrdinal; roundto; decimalPrecision; decimalToDMS; ------------------------------------------------------------------------------- p.getPreferredValue = function(frame) frame.args.rank = "best" return p.getValue(frame) end ------------------------------------------------------------------------------- -- getCoords is used to get coordinates for display in an infobox -- whitelist and blacklist are implemented -- optional 'display' parameter is allowed, defaults to nil - was "inline, title" ------------------------------------------------------------------------------- -- Dependencies: setRanks(); parseInput(); decimalPrecision(); ------------------------------------------------------------------------------- p.getCoords = function(frame) local propertyID = "P625" -- if there is a 'display' parameter supplied, use it -- otherwise default to nothing local disp = frame.args.display or "" if disp == "" then disp = nil -- default to not supplying display parameter, was "inline, title" end -- there may be a format parameter to switch from deg/min/sec to decimal degrees -- default is deg/min/sec -- decimal degrees needs |format = dec local form = (frame.args.format or ""):lower():sub(1,3) if form ~= "dec" then form = "dms" end -- just deal with best values frame.args.reqranks = setRanks("best") local qid, props = parseInput(frame, frame.args[1], propertyID) if not qid then return props -- either local parameter or nothing else local dv = props[1].mainsnak.datavalue.value local lat, long, prec = dv.latitude, dv.longitude, dv.precision lat = decimalPrecision(lat, prec) long = decimalPrecision(long, prec) local lat_long = { lat, long } lat_long["display"] = disp lat_long["format"] = form -- invoke template Coord with the values stored in the table return frame:expandTemplate{title = 'coord', args = lat_long} end end ------------------------------------------------------------------------------- -- getQualifierValue is used to get a formatted value of a qualifier -- -- The call needs: a property (the unnamed parameter or 1=) -- a target value for that property (pval=) -- a qualifier for that target value (qual=) -- The usual whitelisting and blacklisting of the property is implemented -- The boolean onlysourced= parameter can be set to return nothing -- when the property is unsourced (or only sourced to Wikipedia) ------------------------------------------------------------------------------- -- Dependencies: parseParam(); setRanks(); parseInput(); sourced(); -- propertyvalueandquals(); assembleoutput(); -- labelOrId(); i18n.latestdatequalifier(); format_Date(); -- findLang(); makeOrdinal(); roundto(); decimalPrecision(); decimalToDMS(); ------------------------------------------------------------------------------- p.getQualifierValue = function(frame) -- The property ID that will have a qualifier is the first unnamed parameter local propertyID = mw.text.trim(frame.args[1] or "") -- The value of the property we want to match whose qualifier value is to be returned -- is passed in named parameter |pval= local propvalue = frame.args.pval -- The property ID of the qualifier -- whose value is to be returned is passed in named parameter |qual= local qualifierID = frame.args.qual -- A filter can be set like this: filter=P642==Q22674854 local filter, fprop, fval local ftable = mw.text.split(frame.args.filter or "", "==") if ftable[2] then fprop = mw.text.trim(ftable[1]) fval = mw.text.trim(ftable[2]) filter = true end -- onlysourced is a boolean passed to return qualifiers -- only when property values are sourced to something other than Wikipedia -- if nothing or an empty string is passed set it true -- if "false" or "no" or 0 is passed set it false local onlysrc = parseParam(frame.args.onlysourced or frame.args.osd, true) -- set a language object and language code in the frame.args table frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code -- set the requested ranks flags frame.args.reqranks = setRanks(frame.args.rank) -- check for locally supplied parameter in second unnamed parameter -- success means no local parameter and the property exists local qid, props = parseInput(frame, frame.args[2], propertyID) if qid then local out = {} -- Scan through the values of the property -- we want something like property is P793, significant event (in propertyID) -- whose value is something like Q385378, construction (in propvalue) -- then we can return the value(s) of a qualifier such as P580, start time (in qualifierID) for k1, v1 in pairs(props) do if v1.mainsnak.snaktype == "value" and v1.mainsnak.datavalue.type == "wikibase-entityid" then -- It's a wiki-linked value, so check if it's the target (in propvalue) and if it has qualifiers if v1.mainsnak.datavalue.value.id == propvalue and v1.qualifiers then if onlysrc == false or sourced(v1) then -- if we've got this far, we have a (sourced) claim with qualifiers -- which matches the target, so apply the filter and find the value(s) of the qualifier we want if not filter or (v1.qualifiers[fprop] and v1.qualifiers[fprop][1].datavalue.value.id == fval) then local quals = v1.qualifiers[qualifierID] if quals then -- can't reference qualifer, so set onlysourced = "no" (args are strings, not boolean) local qargs = frame.args qargs.onlysourced = "no" local vals = propertyvalueandquals(quals, qargs, qid) for k, v in ipairs(vals) do out[#out + 1] = v end end end end -- of check for sourced end -- of check for matching required value and has qualifiers end -- of check for wikibase entity end -- of loop through values of propertyID return assembleoutput(out, frame.args, qid, propertyID) else return props -- either local parameter or nothing end -- of test for success return nil end ------------------------------------------------------------------------------- -- getSumOfParts scans the property 'has part' (P527) for values matching a list. -- The list is passed in parameter vlist. -- It consists of a string of Qids separated by spaces or any usual punctuation. -- If the matched values have a qualifier 'quantity' (P1114), those quantities are summed. -- The sum is returned as a number or nothing if zero. ------------------------------------------------------------------------------- -- Dependencies: _getsumofparts; ------------------------------------------------------------------------------- p.getSumOfParts = function(frame) local sum = _getsumofparts(frame.args) if sum == 0 then return end return sum end ------------------------------------------------------------------------------- -- getValueByQual gets the value of a property which has a qualifier with a given entity value -- The call needs: -- a property ID (the unnamed parameter or 1=Pxxx) -- the ID of a qualifier for that property (qualID=Pyyy) -- either the Wikibase-entity ID of a value for that qualifier (qvalue=Qzzz) -- or a string value for that qualifier (qvalue=abc123) -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: _getvaluebyqual; parseParam; setRanks; parseInput; sourced; -- assembleoutput; ------------------------------------------------------------------------------- p.getValueByQual = function(frame) local qualID = frame.args.qualID -- The Q-id of the value for the qualifier we want to match is in named parameter |qvalue= local qval = frame.args.qvalue or "" if qval == "" then return "no qualifier value supplied" end local function checkQID(id) return id == qval end return _getvaluebyqual(frame, qualID, checkQID) end ------------------------------------------------------------------------------- -- getValueByLang gets the value of a property which has a qualifier P407 -- ("language of work or name") whose value has the given language code -- The call needs: -- a property ID (the unnamed parameter or 1=Pxxx) -- the MediaWiki language code to match the language (lang=xx[-yy]) -- (if no code is supplied, it uses the default language) -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: _getvaluebyqual; parseParam; setRanks; parseInput; sourced; assembleoutput; ------------------------------------------------------------------------------- p.getValueByLang = function(frame) -- The language code for the qualifier we want to match is in named parameter |lang= local langcode = findLang(frame.args.lang).code local function checkLanguage(id) -- id should represent a language like "British English (Q7979)" -- it should have string property "Wikimedia language code (P424)" -- qlcode will be a table: local qlcode = mw.wikibase.getBestStatements(id, "P424") if (#qlcode > 0) and (qlcode[1].mainsnak.datavalue.value == langcode) then return true end end return _getvaluebyqual(frame, "P407", checkLanguage) end ------------------------------------------------------------------------------- -- getValueByRefSource gets the value of a property which has a reference "stated in" (P248) -- whose value has the given entity-ID. -- The call needs: -- a property ID (the unnamed parameter or 1=Pxxx) -- the entity ID of a value to match where the reference is stated in (match=Qzzz) -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getValueByRefSource = function(frame) -- The property ID that we want to check is the first unnamed parameter local propertyID = mw.text.trim(frame.args[1] or ""):upper() if propertyID == "" then return "no property supplied" end -- The Q-id of the value we want to match is in named parameter |qvalue= local qval = (frame.args.match or ""):upper() if qval == "" then qval = "Q21540096" end local unit = (frame.args.unit or ""):upper() if unit == "" then unit = "Q4917" end local onlysrc = parseParam(frame.args.onlysourced or frame.args.osd, true) -- set the requested ranks flags frame.args.reqranks = setRanks(frame.args.rank) -- set a language object and code in the frame.args table frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local linked = parseParam(frame.args.linked, true) local uabbr = parseParam(frame.args.uabbr or frame.args.unitabbr, false) -- qid not nil means no local parameter and the property exists local qid, props = parseInput(frame, frame.args[2], propertyID) if qid then local out = {} local mlt= {} for k1, v1 in ipairs(props) do if onlysrc == false or sourced(v1) then if v1.references then for k2, v2 in ipairs(v1.references) do if v2.snaks.P248 then for k3, v3 in ipairs(v2.snaks.P248) do if v3.datavalue.value.id == qval then out[#out+1], mlt[#out+1] = rendersnak(v1, frame.args, linked, "", "", "", "", uabbr, unit) if not mlt[#out] then -- we only need one match per property value -- unless datatype was monolingual text break end end -- of test for match end -- of loop through values "stated in" end -- of test that "stated in" exists end -- of loop through references end -- of test that references exist end -- of test for sourced end -- of loop through values of propertyID if #mlt > 0 then local langcode = frame.args.lang langcode = mw.text.split( langcode, '-', true )[1] local fbtbl = mw.language.getFallbacksFor( langcode ) table.insert( fbtbl, 1, langcode ) local bestval = "" local found = false for idx1, lang1 in ipairs(fbtbl) do for idx2, lang2 in ipairs(mlt) do if (lang1 == lang2) and not found then bestval = out[idx2] found = true break end end -- loop through values of property end -- loop through fallback languages if found then -- replace output table with a table containing the best value out = { bestval } else -- more than one value and none of them on the list of fallback languages -- sod it, just give them the first one out = { out[1] } end end return assembleoutput(out, frame.args, qid, propertyID) else return props -- no property or local parameter supplied end -- of test for success end ------------------------------------------------------------------------------- -- getPropertyIDs takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented. -- It returns the Entity-IDs (Qids) of the values of a property if it is a Wikibase-Entity. -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p._getPropertyIDs = function(args) args.reqranks = setRanks(args.rank) args.langobj = findLang(args.lang) args.lang = args.langobj.code -- change default for noicon to true args.noicon = tostring(parseParam(args.noicon or "", true)) local f = {} f.args = args local pid = mw.text.trim(args[1] or ""):upper() -- get the qid and table of claims for the property, or nothing and the local value passed local qid, props = parseInput(f, args[2], pid) if not qid then return props end if not props[1] then return nil end local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local out = {} for i, v in ipairs(props) do local snak = v.mainsnak if ( snak.datatype == "wikibase-item" ) and ( v.rank and args.reqranks[v.rank:sub(1, 1)] ) and ( snak.snaktype == "value" ) and ( sourced(v) or not onlysrc ) then out[#out+1] = snak.datavalue.value.id end if maxvals > 0 and #out >= maxvals then break end end return assembleoutput(out, args, qid, pid) end p.getPropertyIDs = function(frame) local args = frame.args return p._getPropertyIDs(args) end ------------------------------------------------------------------------------- -- getQualifierIDs takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented. -- It takes a property-id as the first unnamed parameter, and an optional parameter qlist -- which is a list of qualifier property-ids to search for (default is "ALL") -- It returns the Entity-IDs (Qids) of the values of a property if it is a Wikibase-Entity. -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getQualifierIDs = function(frame) local args = frame.args args.reqranks = setRanks(args.rank) args.langobj = findLang(args.lang) args.lang = args.langobj.code -- change default for noicon to true args.noicon = tostring(parseParam(args.noicon or "", true)) local f = {} f.args = args local pid = mw.text.trim(args[1] or ""):upper() -- get the qid and table of claims for the property, or nothing and the local value passed local qid, props = parseInput(f, args[2], pid) if not qid then return props end if not props[1] then return nil end -- get the other parameters local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local qlist = args.qlist or "" if qlist == "" then qlist = "ALL" end qlist = qlist:gsub("[%p%s]+", " ") .. " " local out = {} for i, v in ipairs(props) do local snak = v.mainsnak if ( v.rank and args.reqranks[v.rank:sub(1, 1)] ) and ( snak.snaktype == "value" ) and ( sourced(v) or not onlysrc ) then if v.qualifiers then for k1, v1 in pairs(v.qualifiers) do if qlist == "ALL " or qlist:match(k1 .. " ") then for i2, v2 in ipairs(v1) do if v2.datatype == "wikibase-item" and v2.snaktype == "value" then out[#out+1] = v2.datavalue.value.id end -- of test that id exists end -- of loop through qualifier values end -- of test for kq in qlist end -- of loop through qualifiers end -- of test for qualifiers end -- of test for rank value, sourced, and value exists if maxvals > 0 and #out >= maxvals then break end end -- of loop through property values return assembleoutput(out, args, qid, pid) end ------------------------------------------------------------------------------- -- getPropOfProp takes two propertyIDs: prop1 and prop2 (as well as the usual parameters) -- If the value(s) of prop1 are of type "wikibase-item" then it returns the value(s) of prop2 -- of each of those wikibase-items. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p._getPropOfProp = function(args) -- parameter sets for commonly used groups of parameters local paraset = tonumber(args.ps or args.parameterset or 0) if paraset == 1 then -- a common setting args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" elseif paraset == 2 then -- equivalent to raw args.rank = "best" args.fetchwikidata = "ALL" args.onlysourced = "no" args.noicon = "true" args.linked = "no" args.pd = "true" elseif paraset == 3 then -- third set goes here end args.reqranks = setRanks(args.rank) args.langobj = findLang(args.lang) args.lang = args.langobj.code local pid1 = args.prop1 or args.pid1 or "" local pid2 = args.prop2 or args.pid2 or "" if pid1 == "" or pid2 == "" then return nil end local f = {} f.args = args local qid1, statements1 = parseInput(f, args[1], pid1) -- parseInput nulls empty args[1] and returns args[1] if nothing on Wikidata if not qid1 then return statements1 end -- otherwise it returns the qid and a table for the statement local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local qualID = mw.text.trim(args.qual or ""):upper() if qualID == "" then qualID = nil end local out = {} for k, v in ipairs(statements1) do if not onlysrc or sourced(v) then local snak = v.mainsnak if snak.datatype == "wikibase-item" and snak.snaktype == "value" then local qid2 = snak.datavalue.value.id local statements2 = {} if args.reqranks.b then statements2 = mw.wikibase.getBestStatements(qid2, pid2) else statements2 = mw.wikibase.getAllStatements(qid2, pid2) end if statements2[1] then local out2 = propertyvalueandquals(statements2, args, qualID) out[#out+1] = assembleoutput(out2, args, qid2, pid2) end end -- of test for valid property1 value end -- of test for sourced if maxvals > 0 and #out >= maxvals then break end end -- of loop through values of property1 return assembleoutput(out, args, qid1, pid1) end p.getPropOfProp = function(frame) local args= frame.args if not args.prop1 and not args.pid1 then args = frame:getParent().args if not args.prop1 and not args.pid1 then return i18n.errors["No property supplied"] end end return p._getPropOfProp(args) end ------------------------------------------------------------------------------- -- getAwardCat takes most of the usual parameters. If the item has values of P166 (award received), -- then it examines each of those awards for P2517 (category for recipients of this award). -- If it exists, it returns the corresponding category, -- with the item's P734 (family name) as sort key, or no sort key if there is no family name. -- The sort key may be overridden by the parameter |sortkey (alias |sk). -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getAwardCat = function(frame) frame.args.reqranks = setRanks(frame.args.rank) frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local args = frame.args args.sep = " " local pid1 = args.prop1 or "P166" local pid2 = args.prop2 or "P2517" if pid1 == "" or pid2 == "" then return nil end -- locally supplied value: local localval = mw.text.trim(args[1] or "") local qid1, statements1 = parseInput(frame, localval, pid1) if not qid1 then return localval end -- linkprefix (strip quotes) local lp = (args.linkprefix or args.lp or ""):gsub('"', '') -- sort key (strip quotes, hyphens and periods): local sk = (args.sortkey or args.sk or ""):gsub('["-.]', '') -- family name: local famname = "" if sk == "" then local p734 = mw.wikibase.getBestStatements(qid1, "P734")[1] local p734id = p734 and p734.mainsnak.snaktype == "value" and p734.mainsnak.datavalue.value.id or "" famname = mw.wikibase.getSitelink(p734id) or "" -- strip namespace and disambigation local pos = famname:find(":") or 0 famname = famname:sub(pos+1):gsub("%s%(.+%)$", "") if famname == "" then local lbl = mw.wikibase.getLabel(p734id) famname = lbl and mw.text.nowiki(lbl) or "" end end local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 local qualID = mw.text.trim(args.qual or ""):upper() if qualID == "" then qualID = nil end local out = {} for k, v in ipairs(statements1) do if not onlysrc or sourced(v) then local snak = v.mainsnak if snak.datatype == "wikibase-item" and snak.snaktype == "value" then local qid2 = snak.datavalue.value.id local statements2 = {} if args.reqranks.b then statements2 = mw.wikibase.getBestStatements(qid2, pid2) else statements2 = mw.wikibase.getAllStatements(qid2, pid2) end if statements2[1] and statements2[1].mainsnak.snaktype == "value" then local qid3 = statements2[1].mainsnak.datavalue.value.id local sitelink = mw.wikibase.getSitelink(qid3) -- if there's no local sitelink, create the sitelink from English label if not sitelink then local lbl = mw.wikibase.getLabelByLang(qid3, "en") if lbl then if lbl:sub(1,9) == "Category:" then sitelink = mw.text.nowiki(lbl) else sitelink = "Category:" .. mw.text.nowiki(lbl) end end end if sitelink then if sk ~= "" then out[#out+1] = "[[" .. lp .. sitelink .. "|" .. sk .. "]]" elseif famname ~= "" then out[#out+1] = "[[" .. lp .. sitelink .. "|" .. famname .. "]]" else out[#out+1] = "[[" .. lp .. sitelink .. "]]" end -- of check for sort keys end -- of test for sitelink end -- of test for category end -- of test for wikibase item has a value end -- of test for sourced if maxvals > 0 and #out >= maxvals then break end end -- of loop through values of property1 return assembleoutput(out, args, qid1, pid1) end ------------------------------------------------------------------------------- -- getIntersectCat takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented -- It takes two properties, |prop1 and |prop2 (e.g. occupation and country of citizenship) -- Each property's value is a wiki-base entity -- For each value of the first parameter (ranks implemented) it fetches the value's main category -- and then each value of the second parameter (possibly substituting a simpler description) -- then it returns all of the categories representing the intersection of those properties, -- (e.g. Category:Actors from Canada). A joining term may be supplied (e.g. |join=from). -- The item's P734 (family name) is the sort key, or no sort key if there is no family name. -- The sort key may be overridden by the parameter |sortkey (alias |sk). ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput; ------------------------------------------------------------------------------- p.getIntersectCat = function(frame) frame.args.reqranks = setRanks(frame.args.rank) frame.args.langobj = findLang(frame.args.lang) frame.args.lang = frame.args.langobj.code local args = frame.args args.sep = " " args.linked = "no" local pid1 = args.prop1 or "P106" local pid2 = args.prop2 or "P27" if pid1 == "" or pid2 == "" then return nil end local qid, statements1 = parseInput(frame, "", pid1) if not qid then return nil end local qid, statements2 = parseInput(frame, "", pid2) if not qid then return nil end -- topics like countries may have different names in categories from their label in Wikidata local subs_exists, subs = pcall(mw.loadData, "Module:WikidataIB/subs") local join = args.join or "" local onlysrc = parseParam(args.onlysourced or args.osd, true) local maxvals = tonumber(args.maxvals) or 0 -- linkprefix (strip quotes) local lp = (args.linkprefix or args.lp or ""):gsub('"', '') -- sort key (strip quotes, hyphens and periods): local sk = (args.sortkey or args.sk or ""):gsub('["-.]', '') -- family name: local famname = "" if sk == "" then local p734 = mw.wikibase.getBestStatements(qid, "P734")[1] local p734id = p734 and p734.mainsnak.snaktype == "value" and p734.mainsnak.datavalue.value.id or "" famname = mw.wikibase.getSitelink(p734id) or "" -- strip namespace and disambigation local pos = famname:find(":") or 0 famname = famname:sub(pos+1):gsub("%s%(.+%)$", "") if famname == "" then local lbl = mw.wikibase.getLabel(p734id) famname = lbl and mw.text.nowiki(lbl) or "" end end local cat1 = {} for k, v in ipairs(statements1) do if not onlysrc or sourced(v) then -- get the ID representing the value of the property local pvalID = (v.mainsnak.snaktype == "value") and v.mainsnak.datavalue.value.id if pvalID then -- get the topic's main category (P910) for that entity local p910 = mw.wikibase.getBestStatements(pvalID, "P910")[1] if p910 and p910.mainsnak.snaktype == "value" then local tmcID = p910.mainsnak.datavalue.value.id -- use sitelink or the English label for the cat local cat = mw.wikibase.getSitelink(tmcID) if not cat then local lbl = mw.wikibase.getLabelByLang(tmcID, "en") if lbl then if lbl:sub(1,9) == "Category:" then cat = mw.text.nowiki(lbl) else cat = "Category:" .. mw.text.nowiki(lbl) end end end cat1[#cat1+1] = cat end -- of test for topic's main category exists end -- of test for property has vaild value end -- of test for sourced if maxvals > 0 and #cat1 >= maxvals then break end end local cat2 = {} for k, v in ipairs(statements2) do if not onlysrc or sourced(v) then local cat = rendersnak(v, args) if subs[cat] then cat = subs[cat] end cat2[#cat2+1] = cat end if maxvals > 0 and #cat2 >= maxvals then break end end local out = {} for k1, v1 in ipairs(cat1) do for k2, v2 in ipairs(cat2) do if sk ~= "" then out[#out+1] = "[[" .. lp .. v1 .. " " .. join .. " " .. v2 .. "|" .. sk .. "]]" elseif famname ~= "" then out[#out+1] = "[[" .. lp .. v1 .. " " .. join .. " " .. v2 .. "|" .. famname .. "]]" else out[#out+1] = "[[" .. lp .. v1 .. " " .. join .. " " .. v2 .. "]]" end -- of check for sort keys end end args.noicon = "true" return assembleoutput(out, args, qid, pid1) end ------------------------------------------------------------------------------- -- qualsToTable takes most of the usual parameters. -- The usual whitelisting, blacklisting, onlysourced, etc. are implemented. -- A qid may be given, and the first unnamed parameter is the property ID, which is of type wikibase item. -- It takes a list of qualifier property IDs as |quals= -- For a given qid and property, it creates the rows of an html table, -- each row being a value of the property (optionally only if the property matches the value in |pval= ) -- each cell being the first value of the qualifier corresponding to the list in |quals ------------------------------------------------------------------------------- -- Dependencies: parseParam; setRanks; parseInput; sourced; ------------------------------------------------------------------------------- p.qualsToTable = function(frame) local args = frame.args local quals = args.quals or "" if quals == "" then return "" end args.reqranks = setRanks(args.rank) local propertyID = mw.text.trim(args[1] or "") local f = {} f.args = args local entityid, props = parseInput(f, "", propertyID) if not entityid then return "" end args.langobj = findLang(args.lang) args.lang = args.langobj.code local pval = args.pval or "" local qplist = mw.text.split(quals, "%p") -- split at punctuation and make a sequential table for i, v in ipairs(qplist) do qplist[i] = mw.text.trim(v):upper() -- remove whitespace and capitalise end local col1 = args.firstcol or "" if col1 ~= "" then col1 = col1 .. "</td><td>" end local emptycell = args.emptycell or "&nbsp;" -- construct a 2-D array of qualifier values in qvals local qvals = {} for i, v in ipairs(props) do local skip = false if pval ~= "" then local pid = v.mainsnak.datavalue and v.mainsnak.datavalue.value.id if pid ~= pval then skip = true end end if not skip then local qval = {} local vqualifiers = v.qualifiers or {} -- go through list of wanted qualifier properties for i1, v1 in ipairs(qplist) do -- check for that property ID in the statement's qualifiers local qv, qtype if vqualifiers[v1] then qtype = vqualifiers[v1][1].datatype if qtype == "time" then if vqualifiers[v1][1].snaktype == "value" then qv = mw.wikibase.renderSnak(vqualifiers[v1][1]) qv = frame:expandTemplate{title="dts", args={qv}} else qv = "?" end elseif qtype == "url" then if vqualifiers[v1][1].snaktype == "value" then qv = mw.wikibase.renderSnak(vqualifiers[v1][1]) local display = mw.ustring.match( mw.uri.decode(qv, "WIKI"), "([%w ]+)$" ) if display then qv = "[" .. qv .. " " .. display .. "]" end end else qv = mw.wikibase.formatValue(vqualifiers[v1][1]) end end -- record either the value or a placeholder qval[i1] = qv or emptycell end -- of loop through list of qualifiers -- add the list of qualifier values as a "row" in the main list qvals[#qvals+1] = qval end end -- of for each value loop local out = {} for i, v in ipairs(qvals) do out[i] = "<tr><td>" .. col1 .. table.concat(qvals[i], "</td><td>") .. "</td></tr>" end return table.concat(out, "\n") end ------------------------------------------------------------------------------- -- getGlobe takes an optional qid of a Wikidata entity passed as |qid= -- otherwise it uses the linked item for the current page. -- If returns the Qid of the globe used in P625 (coordinate location), -- or nil if there isn't one. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getGlobe = function(frame) local qid = frame.args.qid or frame.args[1] or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end local coords = mw.wikibase.getBestStatements(qid, "P625")[1] local globeid if coords and coords.mainsnak.snaktype == "value" then globeid = coords.mainsnak.datavalue.value.globe:match("(Q%d+)") end return globeid end ------------------------------------------------------------------------------- -- getCommonsLink takes an optional qid of a Wikidata entity passed as |qid= -- It returns one of the following in order of preference: -- the Commons sitelink of the linked Wikidata item; -- the Commons sitelink of the topic's main category of the linked Wikidata item; ------------------------------------------------------------------------------- -- Dependencies: _getCommonslink(); _getSitelink(); parseParam() ------------------------------------------------------------------------------- p.getCommonsLink = function(frame) local oc = frame.args.onlycat or frame.args.onlycategories local fb = parseParam(frame.args.fallback or frame.args.fb, true) return _getCommonslink(frame.args.qid, oc, fb) end ------------------------------------------------------------------------------- -- getSitelink takes the qid of a Wikidata entity passed as |qid= -- It takes an optional parameter |wiki= to determine which wiki is to be checked for a sitelink -- If the parameter is blank, then it uses the local wiki. -- If there is a sitelink to an article available, it returns the plain text link to the article -- If there is no sitelink, it returns nil. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getSiteLink = function(frame) return _getSitelink(frame.args.qid, frame.args.wiki or mw.text.trim(frame.args[1] or "")) end ------------------------------------------------------------------------------- -- getLink has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- If there is a sitelink to an article on the local Wiki, it returns a link to the article -- with the Wikidata label as the displayed text. -- If there is no sitelink, it returns the label as plain text. -- If there is no label in the local language, it displays the qid instead. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getLink = function(frame) local itemID = mw.text.trim(frame.args[1] or frame.args.qid or "") if itemID == "" then return end local sitelink = mw.wikibase.getSitelink(itemID) local label = labelOrId(itemID) if sitelink then return "[[:" .. sitelink .. "|" .. label .. "]]" else return label end end ------------------------------------------------------------------------------- -- getLabel has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- It returns the Wikidata label for the local language as plain text. -- If there is no label in the local language, it displays the qid instead. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getLabel = function(frame) local itemID = mw.text.trim(frame.args[1] or frame.args.qid or "") if itemID == "" then return end local lang = frame.args.lang or "" if lang == "" then lang = nil end local label = labelOrId(itemID, lang) return label end ------------------------------------------------------------------------------- -- label has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- if no qid is supplied, it uses the qid associated with the current page. -- It returns the Wikidata label for the local language as plain text. -- If there is no label in the local language, it returns nil. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.label = function(frame) local qid = mw.text.trim(frame.args[1] or frame.args.qid or "") if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return end local lang = frame.args.lang or "" if lang == "" then lang = nil end local label, success = labelOrId(qid, lang) if success then return label end end ------------------------------------------------------------------------------- -- getAT (Article Title) -- has the qid of a Wikidata entity passed as the first unnamed parameter or as |qid= -- If there is a sitelink to an article on the local Wiki, it returns the sitelink as plain text. -- If there is no sitelink or qid supplied, it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAT = function(frame) local itemID = mw.text.trim(frame.args[1] or frame.args.qid or "") if itemID == "" then return end return mw.wikibase.getSitelink(itemID) end ------------------------------------------------------------------------------- -- getDescription has the qid of a Wikidata entity passed as |qid= -- (it defaults to the associated qid of the current article if omitted) -- and a local parameter passed as the first unnamed parameter. -- Any local parameter passed (other than "Wikidata" or "none") becomes the return value. -- It returns the article description for the Wikidata entity if the local parameter is "Wikidata". -- Nothing is returned if the description doesn't exist or "none" is passed as the local parameter. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getDescription = function(frame) local desc = mw.text.trim(frame.args[1] or "") local itemID = mw.text.trim(frame.args.qid or "") if itemID == "" then itemID = nil end if desc:lower() == 'wikidata' then return mw.wikibase.getDescription(itemID) elseif desc:lower() == 'none' then return nil else return desc end end ------------------------------------------------------------------------------- -- getAliases has the qid of a Wikidata entity passed as |qid= -- (it defaults to the associated qid of the current article if omitted) -- and a local parameter passed as the first unnamed parameter. -- It implements blacklisting and whitelisting with a field name of "alias" by default. -- Any local parameter passed becomes the return value. -- Otherwise it returns the aliases for the Wikidata entity with the usual list options. -- Nothing is returned if the aliases do not exist. ------------------------------------------------------------------------------- -- Dependencies: findLang(); assembleoutput() ------------------------------------------------------------------------------- p.getAliases = function(frame) local args = frame.args local fieldname = args.name or "" if fieldname == "" then fieldname = "alias" end local blacklist = args.suppressfields or args.spf or "" if blacklist:find(fieldname) then return nil end local localval = mw.text.trim(args[1] or "") if localval ~= "" then return localval end local whitelist = args.fetchwikidata or args.fwd or "" if whitelist == "" then whitelist = "NONE" end if not (whitelist == 'ALL' or whitelist:find(fieldname)) then return nil end local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return nil end local aliases = mw.wikibase.getEntity(qid).aliases if not aliases then return nil end args.langobj = findLang(args.lang) local langcode = args.langobj.code args.lang = langcode local out = {} for k1, v1 in pairs(aliases) do if v1[1].language == langcode then for k1, v2 in ipairs(v1) do out[#out+1] = v2.value end break end end return assembleoutput(out, args, qid) end ------------------------------------------------------------------------------- -- pageId returns the page id (entity ID, Qnnn) of the current page -- returns nothing if the page is not connected to Wikidata ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.pageId = function(frame) return mw.wikibase.getEntityIdForCurrentPage() end ------------------------------------------------------------------------------- -- formatDate is a wrapper to export the private function format_Date ------------------------------------------------------------------------------- -- Dependencies: format_Date(); ------------------------------------------------------------------------------- p.formatDate = function(frame) return format_Date(frame.args[1], frame.args.df, frame.args.bc) end ------------------------------------------------------------------------------- -- location is a wrapper to export the private function _location -- it takes the entity-id as qid or the first unnamed parameter -- optional boolean parameter first toggles the display of the first item -- optional boolean parameter skip toggles the display to skip to the last item -- parameter debug=<y/n> (default 'n') adds error msg if not a location ------------------------------------------------------------------------------- -- Dependencies: _location(); ------------------------------------------------------------------------------- p.location = function(frame) local debug = (frame.args.debug or ""):sub(1, 1):lower() if debug == "" then debug = "n" end local qid = mw.text.trim(frame.args.qid or frame.args[1] or ""):upper() if qid == "" then qid=mw.wikibase.getEntityIdForCurrentPage() end if not qid then if debug ~= "n" then return i18n.errors["entity-not-found"] else return nil end end local first = mw.text.trim(frame.args.first or "") local skip = mw.text.trim(frame.args.skip or "") return table.concat( _location(qid, first, skip), ", " ) end ------------------------------------------------------------------------------- -- checkBlacklist implements a test to check whether a named field is allowed -- returns true if the field is not blacklisted (i.e. allowed) -- returns false if the field is blacklisted (i.e. disallowed) -- {{#if:{{#invoke:WikidataIB |checkBlacklist |name=Joe |suppressfields=Dave; Joe; Fred}} | not blacklisted | blacklisted}} -- displays "blacklisted" -- {{#if:{{#invoke:WikidataIB |checkBlacklist |name=Jim |suppressfields=Dave; Joe; Fred}} | not blacklisted | blacklisted}} -- displays "not blacklisted" ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.checkBlacklist = function(frame) local blacklist = frame.args.suppressfields or frame.args.spf or "" local fieldname = frame.args.name or "" if blacklist ~= "" and fieldname ~= "" then if blacklist:find(fieldname) then return false else return true end else -- one of the fields is missing: let's call that "not on the list" return true end end ------------------------------------------------------------------------------- -- emptyor returns nil if its first unnamed argument is just punctuation, whitespace or html tags -- otherwise it returns the argument unchanged (including leading/trailing space). -- If the argument may contain "=", then it must be called explicitly: -- |1=arg -- (In that case, leading and trailing spaces are trimmed) -- It finds use in infoboxes where it can replace tests like: -- {{#if: {{#invoke:WikidatIB |getvalue |P99 |fwd=ALL}} | <span class="xxx">{{#invoke:WikidatIB |getvalue |P99 |fwd=ALL}}</span> | }} -- with a form that uses just a single call to Wikidata: -- {{#invoke |WikidataIB |emptyor |1= <span class="xxx">{{#invoke:WikidataIB |getvalue |P99 |fwd=ALL}}</span> }} ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.emptyor = function(frame) local s = frame.args[1] or "" if s == "" then return nil end local sx = s:gsub("%s", ""):gsub("<[^>]*>", ""):gsub("%p", "") if sx == "" then return nil else return s end end ------------------------------------------------------------------------------- -- labelorid is a public function to expose the output of labelOrId() -- Pass the Q-number as |qid= or as an unnamed parameter. -- It returns the Wikidata label for that entity or the qid if no label exists. ------------------------------------------------------------------------------- -- Dependencies: labelOrId ------------------------------------------------------------------------------- p.labelorid = function(frame) return (labelOrId(frame.args.qid or frame.args[1])) end ------------------------------------------------------------------------------- -- getLang returns the MediaWiki language code of the current content. -- If optional parameter |style=full, it returns the language name. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getLang = function(frame) local style = (frame.args.style or ""):lower() local langcode = mw.language.getContentLanguage().code if style == "full" then return mw.language.fetchLanguageName( langcode ) end return langcode end ------------------------------------------------------------------------------- -- getItemLangCode takes a qid parameter (using the current page's qid if blank) -- If the item for that qid has property country (P17) it looks at the first preferred value -- If the country has an official language (P37), it looks at the first preferred value -- If that official language has a language code (P424), it returns the first preferred value -- Otherwise it returns nothing. ------------------------------------------------------------------------------- -- Dependencies: _getItemLangCode() ------------------------------------------------------------------------------- p.getItemLangCode = function(frame) return _getItemLangCode(frame.args.qid or frame.args[1]) end ------------------------------------------------------------------------------- -- findLanguage exports the local findLang() function -- It takes an optional language code and returns, in order of preference: -- the code if a known language; -- the user's language, if set; -- the server's content language. ------------------------------------------------------------------------------- -- Dependencies: findLang ------------------------------------------------------------------------------- p.findLanguage = function(frame) return findLang(frame.args.lang or frame.args[1]).code end ------------------------------------------------------------------------------- -- getQid returns the qid, if supplied -- failing that, the Wikidata entity ID of the "category's main topic (P301)", if it exists -- failing that, the Wikidata entity ID associated with the current page, if it exists -- otherwise, nothing ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getQid = function(frame) local qid = (frame.args.qid or ""):upper() -- check if a qid was passed; if so, return it: if qid ~= "" then return qid end -- check if there's a "category's main topic (P301)": qid = mw.wikibase.getEntityIdForCurrentPage() if qid then local prop301 = mw.wikibase.getBestStatements(qid, "P301") if prop301[1] then local mctid = prop301[1].mainsnak.datavalue.value.id if mctid then return mctid end end end -- otherwise return the page qid (if any) return qid end ------------------------------------------------------------------------------- -- followQid takes four optional parameters: qid, props, list and all. -- If qid is not given, it uses the qid for the connected page -- or returns nil if there isn't one. -- props is a list of properties, separated by punctuation. -- If props is given, the Wikidata item for the qid is examined for each property in turn. -- If that property contains a value that is another Wikibase-item, that item's qid is returned, -- and the search terminates, unless |all=y when all of the qids are returned, separated by spaces. -- If |list= is set to a template, the qids are passed as arguments to the template. -- If props is not given, the qid is returned. ------------------------------------------------------------------------------- -- Dependencies: parseParam() ------------------------------------------------------------------------------- p._followQid = function(args) local qid = (args.qid or ""):upper() local all = parseParam(args.all, false) local list = args.list or "" if list == "" then list = nil end if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end local out = {} local props = (args.props or ""):upper() if props ~= "" then for p in mw.text.gsplit(props, "%p") do -- split at punctuation and iterate p = mw.text.trim(p) for i, v in ipairs( mw.wikibase.getBestStatements(qid, p) ) do local linkedid = v.mainsnak.datavalue and v.mainsnak.datavalue.value.id if linkedid then if all then out[#out+1] = linkedid else return linkedid end -- test for all or just the first one found end -- test for value exists for that property end -- loop through values of property to follow end -- loop through list of properties to follow end if #out > 0 then local ret = "" if list then ret = mw.getCurrentFrame():expandTemplate{title = list, args = out} else ret = table.concat(out, " ") end return ret else return qid end end p.followQid = function(frame) return p._followQid(frame.args) end ------------------------------------------------------------------------------- -- globalSiteID returns the globalSiteID for the current wiki -- e.g. returns "enwiki" for the English Wikipedia, "enwikisource" for English Wikisource, etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.globalSiteID = function(frame) return mw.wikibase.getGlobalSiteId() end ------------------------------------------------------------------------------- -- siteID returns the root of the globalSiteID -- e.g. "en" for "enwiki", "enwikisource", etc. -- treats "en-gb" as "en", etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.siteID = function(frame) local txtlang = frame:callParserFunction('int', {'lang'}) or "" -- This deals with specific exceptions: be-tarask -> be-x-old if txtlang == "be-tarask" then return "be_x_old" end local pos = txtlang:find("-") local ret = "" if pos then ret = txtlang:sub(1, pos-1) else ret = txtlang end return ret end ------------------------------------------------------------------------------- -- projID returns the code used to link to the reader's language's project -- e.g "en" for [[:en:WikidataIB]] -- treats "en-gb" as "en", etc. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.projID = function(frame) local txtlang = frame:callParserFunction('int', {'lang'}) or "" -- This deals with specific exceptions: be-tarask -> be-x-old if txtlang == "be-tarask" then return "be-x-old" end local pos = txtlang:find("-") local ret = "" if pos then ret = txtlang:sub(1, pos-1) else ret = txtlang end return ret end ------------------------------------------------------------------------------- -- formatNumber formats a number according to the the supplied language code ("|lang=") -- or the default language if not supplied. -- The number is the first unnamed parameter or "|num=" ------------------------------------------------------------------------------- -- Dependencies: findLang() ------------------------------------------------------------------------------- p.formatNumber = function(frame) local lang local num = tonumber(frame.args[1] or frame.args.num) or 0 lang = findLang(frame.args.lang) return lang:formatNum( num ) end ------------------------------------------------------------------------------- -- examine dumps the property (the unnamed parameter or pid) -- from the item given by the parameter 'qid' (or the other unnamed parameter) -- or from the item corresponding to the current page if qid is not supplied. -- e.g. {{#invoke:WikidataIB |examine |pid=P26 |qid=Q42}} -- or {{#invoke:WikidataIB |examine |P26 |Q42}} or any combination of these -- or {{#invoke:WikidataIB |examine |P26}} for the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.examine = function( frame ) local args if frame.args[1] or frame.args.pid or frame.args.qid then args = frame.args else args = frame:getParent().args end local par = {} local pid = (args.pid or ""):upper() local qid = (args.qid or ""):upper() par[1] = mw.text.trim( args[1] or "" ):upper() par[2] = mw.text.trim( args[2] or "" ):upper() table.sort(par) if par[2]:sub(1,1) == "P" then par[1], par[2] = par[2], par[1] end if pid == "" then pid = par[1] end if qid == "" then qid = par[2] end local q1 = qid:sub(1,1) if pid:sub(1,1) ~= "P" then return "No property supplied" end if q1 ~= "Q" and q1 ~= "M" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return "No item for this page" end return "<pre>" .. mw.dumpObject( mw.wikibase.getAllStatements( qid, pid ) ) .. "</pre>" end ------------------------------------------------------------------------------- -- checkvalue looks for 'val' as a wikibase-item value of a property (the unnamed parameter or pid) -- from the item given by the parameter 'qid' -- or from the Wikidata item associated with the current page if qid is not supplied. -- It only checks ranks that are requested (preferred and normal by default) -- If property is not supplied, then P31 (instance of) is assumed. -- It returns val if found or nothing if not found. -- e.g. {{#invoke:WikidataIB |checkvalue |val=Q5 |pid=P31 |qid=Q42}} -- or {{#invoke:WikidataIB |checkvalue |val=Q5 |P31 |qid=Q42}} -- or {{#invoke:WikidataIB |checkvalue |val=Q5 |qid=Q42}} -- or {{#invoke:WikidataIB |checkvalue |val=Q5 |P31}} for the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.checkvalue = function( frame ) local args if frame.args.val then args = frame.args else args = frame:getParent().args end local val = args.val if not val then return nil end local pid = mw.text.trim(args.pid or args[1] or "P31"):upper() local qid = (args.qid or ""):upper() if pid:sub(1,1) ~= "P" then return nil end if qid:sub(1,1) ~= "Q" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end local ranks = setRanks(args.rank) local stats = {} if ranks.b then stats = mw.wikibase.getBestStatements(qid, pid) else stats = mw.wikibase.getAllStatements( qid, pid ) end if not stats[1] then return nil end if stats[1].mainsnak.datatype == "wikibase-item" then for k, v in pairs( stats ) do local ms = v.mainsnak if ranks[v.rank:sub(1,1)] and ms.snaktype == "value" and ms.datavalue.value.id == val then return val end end end return nil end ------------------------------------------------------------------------------- -- url2 takes a parameter url= that is a proper url and formats it for use in an infobox. -- If no parameter is supplied, it returns nothing. -- This is the equivalent of Template:URL -- but it keeps the "edit at Wikidata" pen icon out of the microformat. -- Usually it will take its url parameter directly from a Wikidata call: -- e.g. {{#invoke:WikidataIB |url2 |url={{wdib |P856 |qid=Q23317 |fwd=ALL |osd=no}} }} ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.url2 = function(frame) local txt = frame.args.url or "" if txt == "" then return nil end -- extract any icon local url, icon = txt:match("(.+)&nbsp;(.+)") -- make sure there's at least a space at the end url = (url or txt) .. " " icon = icon or "" -- extract any protocol like https:// local prot = url:match("(https*://).+[ \"\']") -- extract address local addr = "" if prot then addr = url:match("https*://(.+)[ \"\']") or " " else prot = "//" addr = url:match("[^%p%s]+%.(.+)[ \"\']") or " " end -- strip trailing / from end of domain-only url and add <wbr/> before . and / local disp, n = addr:gsub( "^([^/]+)/$", "%1" ):gsub("%/", "<wbr/>/"):gsub("%.", "<wbr/>.") return '<span class="url">[' .. prot .. addr .. " " .. disp .. "]</span>&nbsp;" .. icon end ------------------------------------------------------------------------------- -- getWebsite fetches the Official website (P856) and formats it for use in an infobox. -- This is similar to Template:Official website but with a url displayed, -- and it adds the "edit at Wikidata" pen icon beyond the microformat if enabled. -- A local value will override the Wikidata value. "NONE" returns nothing. -- e.g. {{#invoke:WikidataIB |getWebsite |qid= |noicon= |lang= |url= }} ------------------------------------------------------------------------------- -- Dependencies: findLang(); parseParam(); ------------------------------------------------------------------------------- p.getWebsite = function(frame) local url = frame.args.url or "" if url:upper() == "NONE" then return nil end local urls = {} local quals = {} local qid = frame.args.qid or "" if url and url ~= "" then urls[1] = url else if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return nil end local prop856 = mw.wikibase.getBestStatements(qid, "P856") for k, v in pairs(prop856) do if v.mainsnak.snaktype == "value" then urls[#urls+1] = v.mainsnak.datavalue.value if v.qualifiers and v.qualifiers["P1065"] then -- just take the first archive url (P1065) local au = v.qualifiers["P1065"][1] if au.snaktype == "value" then quals[#urls] = au.datavalue.value end -- test for archive url having a value end -- test for qualifers end -- test for website having a value end -- loop through website(s) end if #urls == 0 then return nil end local out = {} for i, u in ipairs(urls) do local link = quals[i] or u local prot, addr = u:match("(http[s]*://)(.+)") addr = addr or u local disp, n = addr:gsub("%.", "<wbr/>%.") out[#out+1] = '<span class="url">[' .. link .. " " .. disp .. "]</span>" end local langcode = findLang(frame.args.lang).code local noicon = parseParam(frame.args.noicon, false) if url == "" and not noicon then out[#out] = out[#out] .. createicon(langcode, qid, "P856") end local ret = "" if #out > 1 then ret = mw.getCurrentFrame():expandTemplate{title = "ubl", args = out} else ret = out[1] end return ret end ------------------------------------------------------------------------------- -- getAllLabels fetches the set of labels and formats it for display as wikitext. -- It takes a parameter 'qid' for arbitrary access, otherwise it uses the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAllLabels = function(frame) local args = frame.args or frame:getParent().args or {} local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return i18n["entity-not-found"] end local labels = mw.wikibase.getEntity(qid).labels if not labels then return i18n["labels-not-found"] end local out = {} for k, v in pairs(labels) do out[#out+1] = v.value .. " (" .. v.language .. ")" end return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- getAllDescriptions fetches the set of descriptions and formats it for display as wikitext. -- It takes a parameter 'qid' for arbitrary access, otherwise it uses the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAllDescriptions = function(frame) local args = frame.args or frame:getParent().args or {} local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return i18n["entity-not-found"] end local descriptions = mw.wikibase.getEntity(qid).descriptions if not descriptions then return i18n["descriptions-not-found"] end local out = {} for k, v in pairs(descriptions) do out[#out+1] = v.value .. " (" .. v.language .. ")" end return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- getAllAliases fetches the set of aliases and formats it for display as wikitext. -- It takes a parameter 'qid' for arbitrary access, otherwise it uses the current page. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.getAllAliases = function(frame) local args = frame.args or frame:getParent().args or {} local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid or not mw.wikibase.entityExists(qid) then return i18n["entity-not-found"] end local aliases = mw.wikibase.getEntity(qid).aliases if not aliases then return i18n["aliases-not-found"] end local out = {} for k1, v1 in pairs(aliases) do local lang = v1[1].language local val = {} for k1, v2 in ipairs(v1) do val[#val+1] = v2.value end out[#out+1] = table.concat(val, ", ") .. " (" .. lang .. ")" end return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- showNoLinks displays the article titles that should not be linked. ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- p.showNoLinks = function(frame) local out = {} for k, v in pairs(donotlink) do out[#out+1] = k end table.sort( out ) return table.concat(out, "; ") end ------------------------------------------------------------------------------- -- checkValidity checks whether the first unnamed parameter represents a valid entity-id, -- that is, something like Q1235 or P123. -- It returns the strings "true" or "false". -- Change false to nil to return "true" or "" (easier to test with #if:). ------------------------------------------------------------------------------- -- Dependencies: none ------------------------------------------------------------------------------- function p.checkValidity(frame) local id = mw.text.trim(frame.args[1] or "") if mw.wikibase.isValidEntityId(id) then return true else return false end end ------------------------------------------------------------------------------- -- getEntityFromTitle returns the Entity-ID (Q-number) for a given title. -- Modification of Module:ResolveEntityId -- The title is the first unnamed parameter. -- The site parameter determines the site/language for the title. Defaults to current wiki. -- The showdab parameter determines whether dab pages should return the Q-number or nil. Defaults to true. -- Returns the Q-number or nil if it does not exist. ------------------------------------------------------------------------------- -- Dependencies: parseParam ------------------------------------------------------------------------------- function p.getEntityFromTitle(frame) local args=frame.args if not args[1] then args=frame:getParent().args end if not args[1] then return nil end local title = mw.text.trim(args[1]) local site = args.site or "" local showdab = parseParam(args.showdab, true) local qid = mw.wikibase.getEntityIdForTitle(title, site) if qid then local prop31 = mw.wikibase.getBestStatements(qid, "P31")[1] if not showdab and prop31 and prop31.mainsnak.datavalue.value.id == "Q4167410" then return nil else return qid end end end ------------------------------------------------------------------------------- -- getDatePrecision returns the number representing the precision of the first best date value -- for the given property. -- It takes the qid and property ID -- The meanings are given at https://www.mediawiki.org/wiki/Wikibase/DataModel#Dates_and_times -- 0 = 1 billion years .. 6 = millennium, 7 = century, 8 = decade, 9 = year, 10 = month, 11 = day -- Returns 0 (or the second unnamed parameter) if the Wikidata does not exist. ------------------------------------------------------------------------------- -- Dependencies: parseParam; sourced; ------------------------------------------------------------------------------- function p.getDatePrecision(frame) local args=frame.args if not args[1] then args=frame:getParent().args end local default = tonumber(args[2] or args.default) or 0 local prop = mw.text.trim(args[1] or "") if prop == "" then return default end local qid = args.qid or "" if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end if not qid then return default end local onlysrc = parseParam(args.onlysourced or args.osd, true) local stat = mw.wikibase.getBestStatements(qid, prop) for i, v in ipairs(stat) do local prec = (onlysrc == false or sourced(v)) and v.mainsnak.datavalue and v.mainsnak.datavalue.value and v.mainsnak.datavalue.value.precision if prec then return prec end end return default end return p ------------------------------------------------------------------------------- -- List of exported functions ------------------------------------------------------------------------------- --[[ _getValue getValue getPreferredValue getCoords getQualifierValue getSumOfParts getValueByQual getValueByLang getValueByRefSource getPropertyIDs getQualifierIDs getPropOfProp getAwardCat getIntersectCat getGlobe getCommonsLink getSiteLink getLink getLabel label getAT getDescription getAliases pageId formatDate location checkBlacklist emptyor labelorid getLang getItemLangCode findLanguage getQID followQid globalSiteID siteID projID formatNumber examine checkvalue url2 getWebsite getAllLabels getAllDescriptions getAllAliases showNoLinks checkValidity getEntityFromTitle getDatePrecision --]] ------------------------------------------------------------------------------- 24d1c7c6e71b14f4fc20ca1b4ea2301469bea627 Module:WikidataIB/nolinks 828 448 897 896 2023-07-10T15:58:11Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:WikidataIB/nolinks]] Scribunto text/plain local p ={} --[[ The values here are the English sitelinks for items that should not be linked. These 36 are not definitive and may be altered to suit. --]] p.items = { "Australia", "Austria", "Belgium", "Canada", "China", "Denmark", "England", "France", "Germany", "Greece", "Hungary", "Iceland", "India", "Republic of Ireland", "Israel", "Italy", "Jamaica", "Japan", "Luxembourg", "Mexico", "Netherlands", "New Zealand", "Northern Ireland", "Norway", "Poland", "Portugal", "Russia", "Scotland", "South Africa", "Spain", "Sweden", "Switzerland", "Turkey", "United Kingdom", "UK", "United States", "USA", "Wales", } --[[ This provides a convenient way to create a test whether an item is on the list. --]] p.itemsindex = {} for i, v in ipairs(p.items) do p.itemsindex[v] = true end return p d42a1e1cb5d411ab1b578dc0d36aa0266f32b2d6 Module:WikidataIB/titleformats 828 449 899 898 2023-07-10T15:58:12Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:WikidataIB/titleformats]] Scribunto text/plain --[[ To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised, and others are quoted. This submodule lists the entity-ids used in 'instance of' (P31), which allows a module to identify the values that should be formatted. The table p.formats is indexed by entity-id, and contains the value " or '' --]] local p = {} p.italics = { "Q571", -- book "Q13593966", -- literary trilogy "Q277759", -- book series "Q2188189", -- musical work "Q11424", -- film "Q13593818", -- film trilogy "Q24856", -- film series "Q5398426", -- television series "Q482994", -- album "Q169930", -- extended play "Q1760610", -- comic book "Q7889", -- video game "Q7058673", -- video game series "Q25379", -- play "Q2743", -- musical "Q37484", -- epic poem "Q41298", -- magazine } p.quotes = { "Q207628", -- musical composition } p.size = 0 p.formats = {} for i, v in ipairs(p.italics) do p.formats[v] = "''" p.size = p.size + 1 end for i, v in ipairs(p.quotes) do p.formats[v] = '"' p.size = p.size + 1 end return p aecc52ff69e56d315f5b60dc21d02dd94a63dfea Template:Efn 10 450 901 900 2023-07-10T15:58:12Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Efn]] wikitext text/x-wiki <includeonly>{{#if:{{{name|}}} |{{#tag:ref|{{{1|{{{reference|{{{content|{{{text|}}}}}}}}}}}}|name={{{name|}}}|group={{#switch: {{{group|}}} | note | upper-alpha | upper-roman | lower-alpha | lower-greek | lower-roman = {{{group|}}} | #default = lower-alpha }} }} |{{#tag:ref|{{{1|{{{reference|{{{content|{{{text|}}}}}}}}}}}}|group={{#switch: {{{group|}}} | note | upper-alpha | upper-roman | lower-alpha | lower-greek | lower-roman = {{{group|}}} | #default = lower-alpha }} }} }}</includeonly><noinclude> {{documentation}} </noinclude> 6ed4e5c148014b92a23bd51d16f3180881bb876c Template:Notelist 10 451 903 902 2023-07-10T15:58:12Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Notelist]] wikitext text/x-wiki {{reflist|{{{1|{{{colwidth|}}}}}}|refs={{{refs|{{{notes|}}}}}}|group={{#switch: {{{group|}}} | note | upper-alpha | upper-roman | lower-alpha | lower-greek | lower-roman = {{{group|}}} | #default = lower-alpha }}}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using notelist with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Notelist]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | colwidth | group | notes | refs }}<noinclude> {{documentation}}</noinclude> 093e937792d01a5dfc9dd3b5e5deda6b48d0f2be Template:Italic title 10 452 905 904 2023-07-10T15:58:12Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Italic_title]] wikitext text/x-wiki <includeonly>{{#invoke:Italic title|main}}</includeonly><noinclude> {{Documentation}} <!-- Add categories to the /doc subpage and interwikis to Wikidata. --> </noinclude> aa77d0fbc6bb86266d633b290b7ab258cc69c8b8 Template:More citations needed 10 453 907 906 2023-07-10T15:58:13Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:More_citations_needed]] wikitext text/x-wiki {{SAFESUBST:<noinclude />#invoke:Unsubst||date=__DATE__ |$B= {{Ambox | name = {{{name|More citations needed}}} | small = {{#if:{{{small|}}}|left}} | type = content | class = ambox-Refimprove | image = [[File:Question book-new.svg|50x40px|alt=]] | issue = This {{#if:{{{1|}}}|{{{1}}}|article}} '''needs additional citations for [[Wikipedia:Verifiability|verification]]'''. | fix = Please help [{{fullurl:{{FULLPAGENAME}}|action=edit}} improve this article] by [[Help:Referencing for beginners|adding citations to reliable sources]]{{#if:{{{1|}}}|{{sp}}in this {{{1}}}}}. Unsourced material may be challenged and removed.{{#if:{{{find2|{{{unquoted|}}}}}}| <br /><small>{{find sources mainspace|{{#if:{{{find|}}}|{{{find}}}|.}}|{{{find2|{{{unquoted|}}}}}}}}</small> |{{#if:{{{find|}}}|{{#ifeq: {{{find|}}} |none ||<br /><small>{{find sources mainspace|{{{find}}} }}</small>}}|<br /><small>{{find sources mainspace}}</small>}} }} | removalnotice = yes | talk = {{{talk|}}} | date = {{{date|}}} | cat = Articles needing additional references | all = All articles needing additional references }} }}<noinclude> <!-- Please add categories to the /doc subpage, thanks --> {{Documentation}} </noinclude> 81086bb304d099fd880ac87f42069dd039303c05 Module:Italic title 828 454 909 908 2023-07-10T15:58:14Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Italic_title]] Scribunto text/plain -- This module implements {{italic title}}. require('strict') local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg local yesno = require('Module:Yesno') -------------------------------------------------------------------------------- -- ItalicTitle class -------------------------------------------------------------------------------- local ItalicTitle = {} do ---------------------------------------------------------------------------- -- Class attributes and functions -- Things that belong to the class are here. Things that belong to each -- object are in the constructor. ---------------------------------------------------------------------------- -- Keys of title parts that can be italicized. local italicizableKeys = { namespace = true, title = true, dab = true, } ---------------------------------------------------------------------------- -- ItalicTitle constructor -- This contains all the dynamic attributes and methods. ---------------------------------------------------------------------------- function ItalicTitle.new() local obj = {} -- Function for checking self variable in methods. local checkSelf = libraryUtil.makeCheckSelfFunction( 'ItalicTitle', 'obj', obj, 'ItalicTitle object' ) -- Checks a key is present in a lookup table. -- Param: name - the function name. -- Param: argId - integer position of the key in the argument list. -- Param: key - the key. -- Param: lookupTable - the table to look the key up in. local function checkKey(name, argId, key, lookupTable) if not lookupTable[key] then error(string.format( "bad argument #%d to '%s' ('%s' is not a valid key)", argId, name, key ), 3) end end -- Set up object structure. local parsed = false local categories = {} local italicizedKeys = {} local italicizedSubstrings = {} -- Parses a title object into its namespace text, title, and -- disambiguation text. -- Param: options - a table of options with the following keys: -- title - the title object to parse -- ignoreDab - ignore any disambiguation parentheses -- Returns the current object. function obj:parseTitle(options) checkSelf(self, 'parseTitle') checkType('parseTitle', 1, options, 'table') checkTypeForNamedArg('parseTitle', 'title', options.title, 'table') local title = options.title -- Title and dab text local prefix, parentheses if not options.ignoreDab then prefix, parentheses = mw.ustring.match( title.text, '^(.+) %(([^%(%)]+)%)$' ) end if prefix and parentheses then self.title = prefix self.dab = parentheses else self.title = title.text end -- Namespace local namespace = mw.site.namespaces[title.namespace].name if namespace and #namespace >= 1 then self.namespace = namespace end -- Register the object as having parsed a title. parsed = true return self end -- Italicizes part of the title. -- Param: key - the key of the title part to be italicized. Possible -- keys are contained in the italicizableKeys table. -- Returns the current object. function obj:italicize(key) checkSelf(self, 'italicize') checkType('italicize', 1, key, 'string') checkKey('italicize', 1, key, italicizableKeys) italicizedKeys[key] = true return self end -- Un-italicizes part of the title. -- Param: key - the key of the title part to be un-italicized. Possible -- keys are contained in the italicizableKeys table. -- Returns the current object. function obj:unitalicize(key) checkSelf(self, 'unitalicize') checkType('unitalicize', 1, key, 'string') checkKey('unitalicize', 1, key, italicizableKeys) italicizedKeys[key] = nil return self end -- Italicizes a substring in the title. This only affects the main part -- of the title, not the namespace or the disambiguation text. -- Param: s - the substring to be italicized. -- Returns the current object. function obj:italicizeSubstring(s) checkSelf(self, 'italicizeSubstring') checkType('italicizeSubstring', 1, s, 'string') italicizedSubstrings[s] = true return self end -- Un-italicizes a substring in the title. This only affects the main -- part of the title, not the namespace or the disambiguation text. -- Param: s - the substring to be un-italicized. -- Returns the current object. function obj:unitalicizeSubstring(s) checkSelf(self, 'unitalicizeSubstring') checkType('unitalicizeSubstring', 1, s, 'string') italicizedSubstrings[s] = nil return self end -- Renders the object into a page name. If no title has yet been parsed, -- the current title is used. -- Returns string function obj:renderTitle() checkSelf(self, 'renderTitle') -- Italicizes a string -- Param: s - the string to italicize -- Returns string. local function italicize(s) assert(type(s) == 'string', 's was not a string') assert(s ~= '', 's was the empty string') return string.format('<i>%s</i>', s) end -- Escape characters in a string that are magic in Lua patterns. -- Param: pattern - the pattern to escape -- Returns string. local function escapeMagicCharacters(s) assert(type(s) == 'string', 's was not a string') return s:gsub('%p', '%%%0') end -- If a title hasn't been parsed yet, parse the current title. if not parsed then self:parseTitle{title = mw.title.getCurrentTitle()} end -- Italicize the different parts of the title and store them in a -- titleParts table to be joined together later. local titleParts = {} -- Italicize the italicizable keys. for key in pairs(italicizableKeys) do if self[key] then if italicizedKeys[key] then titleParts[key] = italicize(self[key]) else titleParts[key] = self[key] end end end -- Italicize substrings. If there are any substrings to be -- italicized then start from the raw title, as this overrides any -- italicization of the main part of the title. if next(italicizedSubstrings) then titleParts.title = self.title for s in pairs(italicizedSubstrings) do local pattern = escapeMagicCharacters(s) local italicizedTitle, nReplacements = titleParts.title:gsub( pattern, italicize ) titleParts.title = italicizedTitle -- If we didn't make any replacements then it means that we -- have been passed a bad substring or that the page has -- been moved to a bad title, so add a tracking category. if nReplacements < 1 then categories['Pages using italic title with no matching string'] = true end end end -- Assemble the title together from the parts. local ret = '' if titleParts.namespace then ret = ret .. titleParts.namespace .. ':' end ret = ret .. titleParts.title if titleParts.dab then ret = ret .. ' (' .. titleParts.dab .. ')' end return ret end -- Returns an expanded DISPLAYTITLE parser function called with the -- result of obj:renderTitle, plus any other optional arguments. -- Returns string function obj:renderDisplayTitle(...) checkSelf(self, 'renderDisplayTitle') return mw.getCurrentFrame():callParserFunction( 'DISPLAYTITLE', self:renderTitle(), ... ) end -- Returns an expanded DISPLAYTITLE parser function called with the -- result of obj:renderTitle, plus any other optional arguments, plus -- any tracking categories. -- Returns string function obj:render(...) checkSelf(self, 'render') local ret = self:renderDisplayTitle(...) for cat in pairs(categories) do ret = ret .. string.format( '[[Category:%s]]', cat ) end return ret end return obj end end -------------------------------------------------------------------------------- -- Exports -------------------------------------------------------------------------------- local p = {} local function getArgs(frame, wrapper) assert(type(wrapper) == 'string', 'wrapper was not a string') return require('Module:Arguments').getArgs(frame, { wrappers = wrapper }) end -- Main function for {{italic title}} function p._main(args) checkType('_main', 1, args, 'table') local italicTitle = ItalicTitle.new() italicTitle:parseTitle{ title = mw.title.getCurrentTitle(), ignoreDab = yesno(args.all, false) } if args.string then italicTitle:italicizeSubstring(args.string) else italicTitle:italicize('title') end return italicTitle:render(args[1]) end function p.main(frame) return p._main(getArgs(frame, 'Template:Italic title')) end function p._dabonly(args) return ItalicTitle.new() :italicize('dab') :render(args[1]) end function p.dabonly(frame) return p._dabonly(getArgs(frame, 'Template:Italic dab')) end return p 9b49016c8f526f680e9ecdaf36ec4ceafda2a2f8 Template:EditOnWikidata 10 455 911 910 2023-07-10T15:58:14Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:EditOnWikidata]] wikitext text/x-wiki #REDIRECT [[Template:Edit on Wikidata]] {{Redirect category shell| {{R from move}} }} da7377f33f3901e6962fbca394dbf9adbae353a6 Template:Edit on Wikidata 10 456 913 912 2023-07-10T15:58:14Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Edit_on_Wikidata]] wikitext text/x-wiki {{#switch: {{{noicon|}}} | no = | false = | #default = <div class="metadata" style="text-align: right;">&#91;[[d:{{#if:{{{qid|}}}|{{{qid}}}|{{#invoke:WikidataIB|pageId}}}}|edit on Wikidata]]]</div> }}<noinclude> {{Documentation}}</noinclude> 18e6ce3f005ce4f424aeba196432645ded6036bb Template:If first display both 10 457 915 914 2023-07-10T15:58:14Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:If_first_display_both]] wikitext text/x-wiki {{#if:{{{1|}}}|{{{1|}}}{{{2|}}}}}<noinclude> {{documentation}} </noinclude> 4d500383f7b4a06011f966ad5667428a065b296f Template:Infobox video game 10 458 917 916 2023-07-10T15:58:15Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Infobox_video_game]] wikitext text/x-wiki {{main other|{{short description|2=noreplace|{{if both|{{{released|{{{release|}}}}}}|{{#invoke:String|match|{{{released|{{{release|}}}}}}|[12]%d%d%d|match=1|nomatch=}}|{{#invoke:String|match|{{{released|{{{release|}}}}}}|[12]%d%d%d|match=1|nomatch=}} video game|Video game}}}}}}{{#invoke:infobox|infoboxTemplate <!-- Start and styling --> | child = {{{child|}}} | subbox = {{{subbox|}}} | bodyclass = ib-video-game hproduct {{#ifeq:{{{collapsible|}}}|yes|collapsible {{#if:{{{state|}}}|{{{state}}}|autocollapse}}}} | templatestyles = Infobox video game/styles.css | aboveclass = fn | italic title = {{{italic title|<noinclude>no</noinclude>}}} <!-- Title --> | above = <includeonly>{{{title|{{#if:{{#invoke:WikidataIB|label}}|{{#invoke:WikidataIB|label}}|{{PAGENAMEBASE}}}}}}}</includeonly> <!-- Image --> | image = {{#invoke:InfoboxImage|InfoboxImage|image={{#invoke:WikidataIB |getValue|rank=best|P18 |name=image |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=no |noicon=yes|{{{image|}}}}}|size={{{image size|{{{image_size|{{{imagesize|}}}}}}}}}|sizedefault=frameless|upright={{{image_upright|1}}}|alt={{{alt|}}}|border={{{border|}}}|suppressplaceholder=yes}} | caption = {{#if:{{{image|}}}|{{{caption|}}}|{{{caption|{{#invoke:WikidataIB|getValue|P18|qual=P2096|qualsonly=y|fwd=ALL}}}}}}} <!-- Start of content --> | label2 = [[Video game developer|Developer(s)]] | data2 = {{{developer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P178|qid={{{qid|}}}|name=developer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{developer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P178}}}}}}}}} | label3 = [[Video game publisher|Publisher(s)]] | data3 = {{{publisher|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P123|qid={{{qid|}}}|name=publisher|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{publisher|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P123}}}}}}}}} | label4 = [[Video game creative director|Director(s)]] | data4 = {{{director|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P57|qid={{{qid|}}}|name=director|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{director|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P57}}}}}}}}} | label5 = [[Video game producer|Producer(s)]] | data5 = {{{producer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P162|qid={{{qid|}}}|name=producer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{producer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P162}}}}}}}}} | label6 = [[Video game designer|Designer(s)]] | data6 = {{{designer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P287|qid={{{qid|}}}|name=designer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{designer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P287}}}}}}}}} | label7 = [[Video game programmer|Programmer(s)]] | data7 = {{{programmer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P943|qid={{{qid|}}}|name=programmer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{programmer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P943}}}}}}}}} | label8 = [[Video game artist|Artist(s)]] | data8 = {{{artist|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P3080|qid={{{qid|}}}|name=artist|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{artist|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P3080}}}}}}}}} | label9 = [[Video game writer|Writer(s)]] | data9 = {{{writer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P50|qid={{{qid|}}}|name=writer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{writer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P50}}}}}}}}} | label10 = [[Video game composer|Composer(s)]] | data10 = {{{composer|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P86|qid={{{qid|}}}|name=composer|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{composer|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P86}}}}}}}}} | label11 = Series | data11 = {{{series|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P179|qid={{{qid|}}}|name=series|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|prefix=''|postfix=''|{{{series|}}}}}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P179}}}}}}}}} | label12 = [[Game engine|Engine]] | data12 = {{{engine|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P408|qid={{{qid|}}}|name=engine|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{engine|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P408}}}}}}}}} | label13 = [[Computing platform|Platform(s)]] | data13 = {{{platform|{{{platforms|{{If first display both|{{#invoke:WikidataIB|getValue|rank=best|P400|qid={{{qid|}}}|name=platform|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{platform|{{{platforms|}}}}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P400}}}}}}}}}}}} | label14 = Release | data14 = {{{released|{{{release|}}}}}} | label15 = [[Video game genre|Genre(s)]] | data15 = {{{genre|{{If first display both|{{#invoke:String2 |ucfirst |{{#invoke:WikidataIB|getValue|rank=best|P136|qid={{{qid|}}}|name=genre|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|shortname=yes|{{{genre|}}} }} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P136}}}}}}}}} | label16 = Mode(s) | data16 = {{{modes|{{If first display both|{{#invoke:String2 |ucfirst |{{#invoke:WikidataIB|getValue|rank=best|P404|qid={{{qid|}}}|name=modes|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|shortname=yes|{{{modes|}}} }} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P404}}}}}}}}} | label17 = [[Arcade system board|Arcade system]] | data17 = {{{arcade system|{{If first display both|{{#invoke:WikidataIB|getQualifierValue|P2670|pval=Q631229|qual=P31|qid={{{qid|}}}|name=arcade_system|suppressfields={{{suppressfields|}}}|fetchwikidata={{{fetchwikidata|ALL}}}|onlysourced={{{onlysourced|yes}}}|noicon={{{noicon|no}}}|list=ubl|sorted=yes|{{{arcade system|}}} }}|{{#ifeq:{{{refs|no}}}|yes|{{wikidata|references|normal+|{{{qid|}}}|P2670|Q631229}}}}}}}}} <!-- For embedded infoboxes --> | data30 = {{{embedded|}}} | below = <includeonly>{{EditOnWikidata|noicon={{{noicon|no}}}|qid={{{qid|}}}}}</includeonly> <!-- Checking code and closing --> }}{{main other|{{#ifeq:{{lc:{{{italic title|}}}}}|no||{{italic title|force={{#ifeq:{{lc:{{{italic title|}}}}}|force|true}}}}}} }}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox video game with unknown parameters|_VALUE_{{PAGENAME}}]]}}|ignoreblank=1|preview=Page using [[Template:Infobox video game]] with unknown parameter "_VALUE_"| alt | arcade system | artist | caption | border | child | collapsible | commons | composer | designer | developer | director | embedded | engine | fetchwikidata | genre | image | image_size | image_upright | italic title | modes | noicon | onlysourced | platform | platforms | producer | programmer | publisher | qid | refs | release | released | series | state | subbox | suppressfields | title | writer }}<includeonly>{{main other|{{#if:{{safesubst:#invoke:Check for unknown parameters|check|unknown=1|preview=1|embedded|image}}|[[Category:Articles using Infobox video game using locally defined parameters]]|[[Category:Articles with infoboxes completely from Wikidata]]}}}}{{#if:{{{image|}}}|[[Category:Articles using Wikidata infoboxes with locally defined images]]}}</includeonly><noinclude><!-- NOTE: The {{#if:1| ... }} syntax allows for list markup to be included in the data fields --> {{documentation}}<!-- Add cats and interwikis to the /doc subpage, not here! --> </noinclude> 060749360e5c0d0d45f6a711d91d2e0fb8f42a55 Template:Vgrelease 10 459 919 918 2023-07-10T15:58:15Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Vgrelease]] wikitext text/x-wiki #REDIRECT [[Template:Video game release]] cc02016a051e4f709be3edd3e9855ca3079d45d6 Template:Video game release 10 460 921 920 2023-07-10T15:58:16Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Video_game_release]] wikitext text/x-wiki {{#invoke:Video game release|main}}<noinclude> {{Documentation}} </noinclude> b303d4187e71b065d565c75cb332c2da8f6e30bd Module:CountryData 828 461 923 922 2023-07-10T15:58:16Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:CountryData]] Scribunto text/plain local p = {} local mostUsed = mw.loadData('Module:CountryData/summary') local function getcontents(frame,country,params) return frame:expandTemplate({title="Country data "..country;args=params}) end function p.getcachedtable(frame, country, params) country = mostUsed.redirects[country] or country if params and next(params) then return p.gettable(frame, country, params) end -- Uses mw.loadData to cache data for the most-used templates if mostUsed.pages[country] then local cache = mw.loadData('Module:CountryData/cache' .. mostUsed.pages[country]) if cache.data[country] then return cache.data[country] end end -- if not in cache return p.gettable(frame, country, params) end function p.gettable(frame,country,params) --Returns the parameters of a country data template as a Lua table --If not a valid data template, return empty table local bool, s = pcall(getcontents,frame,country,params or {}) if bool and (string.find(s,"^%{%{ *%{%{%{1") or string.find(s,"^%{%{safesubst: *%{%{%{1")) then --Replace parameter delimiters with arbitrary control characters --to avoid clashes if param values contain equals/pipe signs s = string.gsub(s,"|([^|=]-)=","\1\1%1\2") s = string.gsub(s,"}}%s*$","\1") --Loop over string and add params to table local part = {} for par in string.gmatch(s,"\1[^\1\2]-\2[^\1\2]-\1") do local k = string.match(par,"\1%s*(.-)%s*\2") local v = string.match(par,"\2%s*(.-)%s*\1") if v and not (v=="" and string.find(k,"^flag alias")) then part[k] = v end end return part else return {} end end function p.getalias(frame) --Returns a single parameter value from a data template local part = p.gettable(frame,frame.args[1]) if frame.args.variant then return tostring(part[frame.args[2].."-"..frame.args.variant] or part[frame.args[2]] or frame.args.def) else return tostring(part[frame.args[2]] or frame.args.def) end end function p.gettemplate(frame) --For testing, recreates the country data from the created Lua table --Get data table local data = p.gettable(frame,frame.args[1]) --Concatenate fields into a template-like string local out = "{{ {{{1}}}" for k,v in pairs(data) do out = out.."\n| "..k.." = "..v end return out.."\n}}" end return p 69ae4a41658bbbdd1c559e87420fcbd21eb437f4 Module:CountryData/summary 828 462 925 924 2023-07-10T15:58:17Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:CountryData/summary]] Scribunto text/plain local p = {} p.pages = {Afghanistan='K', Albania='H', Algeria='F', Angola='K', Argentina='B', Armenia='G', Australia='A', Austria='B', Azerbaijan='F', Bahrain='K', Bangladesh='I', Belarus='D', Belgium='B', Bolivia='J', Bosnia_and_Herzegovina='F', Brazil='B', Bulgaria='D', Cameroon='I', Canada='A', Chile='D', China='B', Chinese_Taipei='H', Colombia='D', Costa_Rica='I', Croatia='D', Cuba='G', Cyprus='G', Czech_Republic='B', Czechoslovakia='H', Democratic_Republic_of_the_Congo='K', Denmark='C', Dominican_Republic='H', East_Germany='K', Ecuador='G', Egypt='E', El_Salvador='J', England='B', Estonia='E', Ethiopia='J', Fiji='K', Finland='C', France='A', Germany='A', Ghana='G', Great_Britain='C', Greece='D', Guatemala='J', Honduras='K', Hong_Kong='F', Hungary='C', Iceland='G', India='A', Indonesia='E', Iran='B', Iraq='J', Ireland='E', Israel='D', Italy='A', Ivory_Coast='I', Jamaica='G', Japan='A', Jordan='K', Kazakhstan='E', Kenya='H', Kuwait='K', Latvia='F', Lebanon='J', Lithuania='F', Luxembourg='G', Malaysia='E', Mali='K', Malta='I', Mexico='C', Moldova='I', Montenegro='H', Morocco='F', Myanmar='J', Nepal='I', Netherlands='A', New_Zealand='C', Nigeria='E', North_Korea='J', North_Macedonia='I', Northern_Ireland='J', Norway='C', Pakistan='F', Panama='I', Paraguay='G', Peru='E', Philippines='E', Poland='A', Portugal='C', Puerto_Rico='H', Qatar='I', Republic_of_Ireland='F', Romania='C', Russia='A', Saudi_Arabia='H', Scotland='D', Senegal='I', Serbia='D', Singapore='F', Slovakia='D', Slovenia='D', South_Africa='C', South_Korea='C', Soviet_Union='E', Spain='A', Sri_Lanka='H', Sweden='B', Switzerland='B', Syria='J', Taiwan='K', Tanzania='K', Thailand='D', Trinidad_and_Tobago='J', Tunisia='G', Turkey='B', Uganda='J', Ukraine='C', United_Arab_Emirates='H', United_Kingdom='B', United_States='A', Uruguay='F', Uzbekistan='H', Venezuela='E', Vietnam='G', Wales='E', West_Germany='G', Yugoslavia='H', Zimbabwe='I', ['Georgia_(country)']='F'} p.redirects = {AFG='Afghanistan', ALB='Albania', ALG='Algeria', AND='Andorra', ANG='Angola', ARG='Argentina', ARM='Armenia', ARU='Aruba', ATG='Antigua and Barbuda', AUS='Australia', AUT='Austria', AZE='Azerbaijan', BAH='Bahamas', BAN='Bangladesh', BAR='Barbados', BDI='Burundi', BEL='Belgium', BEN='Benin', BER='Bermuda', BFA='Burkina Faso', BHR='Bahrain', BIH='Bosnia and Herzegovina', BLR='Belarus', BOL='Bolivia', BOT='Botswana', BRA='Brazil', BRU='Brunei', BUL='Bulgaria', BWA='Botswana', CAM='Cambodia', CAN='Canada', CAY='Cayman Islands', CGO='Republic of the Congo', CHE='Switzerland', CHI='Chile', CHL='Chile', CHN='China', CIS='Commonwealth of Independent States', CIV='Ivory Coast', CMR='Cameroon', COD='Democratic Republic of the Congo', COK='Cook Islands', COL='Colombia', CPV='Cape Verde', CRC='Costa Rica', CRO='Croatia', CSA='Confederate States of America', CSK='Czechoslovakia', CUB='Cuba', CUR='Curaçao', CYP='Cyprus', CZE='Czech Republic', Congo='Republic of the Congo', DEN='Denmark', DEU='Germany', DNK='Denmark', DOM='Dominican Republic', DRC='Democratic Republic of the Congo', ECU='Ecuador', EGY='Egypt', ENG='England', ESA='El Salvador', ESP='Spain', EST='Estonia', ETH='Ethiopia', EU='European Union', FIJ='Fiji', FIN='Finland', FJI='Fiji', FRA='France', FRG='West Germany', FRO='Faroe Islands', GAB='Gabon', GAM='Gambia', GBR='Great Britain', GDR='East Germany', GEO='Georgia (country)', GER='Germany', GHA='Ghana', GIB='Gibraltar', GRC='Greece', GRE='Greece', GRN='Grenada', GUA='Guatemala', GUI='Guinea', GUM='Guam', GUY='Guyana', Georgia='Georgia (country)', HAI='Haiti', HKG='Hong Kong', HON='Honduras', HRV='Croatia', HUN='Hungary', IDN='Indonesia', INA='Indonesia', IND='India', IRE='Ireland', IRI='Iran', IRL='Republic of Ireland', IRN='Iran', IRQ='Iraq', ISL='Iceland', ISR='Israel', ITA='Italy', JAM='Jamaica', JOR='Jordan', JP='Japan', JPN='Japan', KAZ='Kazakhstan', KEN='Kenya', KGZ='Kyrgyzstan', KOR='South Korea', KOS='Kosovo', KSA='Saudi Arabia', KUW='Kuwait', LAO='Laos', LAT='Latvia', LBN='Lebanon', LBR='Liberia', LBY='Libya', LCA='Saint Lucia', LIB='Lebanon', LIE='Liechtenstein', LIT='Lithuania', LTU='Lithuania', LUX='Luxembourg', LVA='Latvia', MAC='Macau', MAD='Madagascar', MAR='Morocco', MAS='Malaysia', MCO='Monaco', MDA='Moldova', MDV='Maldives', MEX='Mexico', MGL='Mongolia', MKD='North Macedonia', MLI='Mali', MLT='Malta', MNE='Montenegro', MON='Monaco', MOZ='Mozambique', MRI='Mauritius', MYA='Myanmar', MYS='Malaysia', NAM='Namibia', NCA='Nicaragua', NCL='New Caledonia', NED='Netherlands', NEP='Nepal', NGA='Nigeria', NGR='Nigeria', NIC='Nicaragua', NIG='Niger', NIR='Northern Ireland', NLD='Netherlands', NOR='Norway', NZ='New Zealand', NZL='New Zealand', OMA='Oman', PAK='Pakistan', PAN='Panama', PAR='Paraguay', PER='Peru', PHI='Philippines', PHL='Philippines', PLE='Palestine', PNG='Papua New Guinea', POL='Poland', POR='Portugal', PRC='China', PRI='Puerto Rico', PRK='North Korea', PRT='Portugal', PUR='Puerto Rico', QAT='Qatar', ROC='Republic of China', ROM='Romania', ROU='Romania', RSA='South Africa', RUS='Russia', RWA='Rwanda', SAM='Samoa', SCG='Serbia and Montenegro', SCO='Scotland', SEN='Senegal', SER='Serbia', SGP='Singapore', SIN='Singapore', SKN='Saint Kitts and Nevis', SLE='Sierra Leone', SLO='Slovenia', SLV='El Salvador', SMR='San Marino', SPA='Spain', SRB='Serbia', SRI='Sri Lanka', SUD='Sudan', SUI='Switzerland', SUR='Suriname', SVK='Slovakia', SVN='Slovenia', SWE='Sweden', SWI='Switzerland', SYR='Syria', TAN='Tanzania', TCH='Czechoslovakia', THA='Thailand', TJK='Tajikistan', TKM='Turkmenistan', TOG='Togo', TON='Tonga', TPE='Chinese Taipei', TRI='Trinidad and Tobago', TTO='Trinidad and Tobago', TUN='Tunisia', TUR='Turkey', TWN='Taiwan', UAE='United Arab Emirates', UGA='Uganda', UK='United Kingdom', UKGBI='United Kingdom of Great Britain and Ireland', UKR='Ukraine', UN='United Nations', URS='Soviet Union', URU='Uruguay', US='United States', USA='United States', USSR='Soviet Union', UZB='Uzbekistan', VAN='Vanuatu', VEN='Venezuela', VIE='Vietnam', WAL='Wales', WIN='West Indies', Washington='Washington (state)', YEM='Yemen', YUG='Yugoslavia', ZAF='South Africa', ZAM='Zambia', ZIM='Zimbabwe', ['Côte d\'Ivoire']='Ivory Coast', ['DR Congo']='Democratic Republic of the Congo', ['New York']='New York (state)', ['People\'s Republic of China']='China', ['SFR Yugoslavia']='Yugoslavia', ['Timor-Leste']='East Timor', ['United States Virgin Islands']='U.S. Virgin Islands', ['United States of America']='United States'} return p 083be15b6e411ffe1f6456c148ba0dbba7ccb330 Module:Video game release 828 463 927 926 2023-07-10T15:58:17Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Video_game_release]] Scribunto text/plain require('strict') local getArgs = require('Module:Arguments').getArgs local cd = require('Module:CountryData') local list = require('Module:List'); local p = {} local knownargs = { ['format'] = true, ['class'] = true, ['style'] = true, ['list_style'] = true, ['item_style'] = true, ['item1_style'] = true, ['indent'] = true } local labels = { ['NA'] = "[[North America|NA]]", ['EU'] = "[[Europe|EU]]", ['EUR'] = "[[Europe|EU]]", ['AU'] = "[[Australasia|AU]]", ['AUS'] = "[[Australasia|AU]]", ['PAL'] = "[[PAL region|PAL]]", ['SEA'] = "[[Southeast Asia|SEA]]", ['AS'] = "[[Asia|AS]]", ['SA'] = "[[South America|SA]]", ['OC'] = "[[Oceania|OC]]", ['WW'] = "<abbr title=\"Worldwide\">WW</abbr>" } local function getLocalLabel(alias) local label = labels[string.upper(alias)] return label end local countryData = {}; -- Used to store country data to avoid the need of repeated calls to Module:CountryData. This saves a little time if the same abbreviation appears multiple times in the template. local function getCountryData(frame, alias) local ualias = string.upper(alias) if (countryData[ualias] == nil) then local cdtable = cd.gettable(frame, alias, {}) countryData[ualias] = cdtable['alias'] end return countryData[ualias] end local function splitLabel(s) local islist = true local res = {} for k,v in ipairs(mw.text.split(s or '', '%s*/%s*')) do local v1 = v:match('^%s*([A-Z][A-Z][A-Z]?)%s*$') if v1 then table.insert(res,v1) else local v2 = v:match('^%s*(%[%[[^%[%]|]*|[A-Z][A-Z][A-Z]?%]%])%s*$') if v2 then table.insert(res,v2) else islist = false end end end return islist and res or {s} end function p.main(frame) local args = getArgs(frame) local listformat = args['format'] if (listformat == nil or listformat == "") then listformat = "unbulleted" end local items = {} -- Old syntax "Two parameter region" use case, where param 1 is an article, param 2 is a label, and param 3 is the date. We assume this case if argument 4 is nil. if (args[3] ~= nil and args[4] == nil) then local item = "<span style=\"font-size:95%;\">[[" if (args[1] ~= nil) then item = item .. args[1] end item = item .. "|" if (args[2] ~= nil) then item = item .. args[2] end item = item .. "]]:</span> " .. args[3] .. "[[Category:Pages using vgrelease with two parameter region]]" table.insert(items, item) -- Old syntax "Blank region" use case, where param 1 is empty, and param 2 is the date. elseif (args[1] == nil and args[2] ~= nil) then local item = args[2] .. "[[Category:Pages using vgrelease without a region]]" table.insert(items, item) -- Normal use cases, region/date pairs in 1/2, 3/4, 5/6, etc. else local i = 1 local j = 2 while (args[i] and args[j]) do local labels = {} for k,v in ipairs(splitLabel(args[i])) do local label = getLocalLabel(v); -- Didn't find a local label? Check for country data. if (label == nil) then if not v:match('^%s*%[') then label = getCountryData(frame, v) end -- Found something? Build a sitelink with it. if (label ~= nil) then label = "[[" .. label .. "|" .. v .. "]]" else label = v end end table.insert(labels, label) end local item = "<span style=\"font-size:95%;\">" .. table.concat(labels,'/') .. ":</span> " .. args[j] table.insert(items, item) i = i + 2 j = j + 2 end end -- Add known parameters of Module:List to the table for k, v in pairs(args) do if (knownargs[k] == true) then items[k] = v end end local out = list.makeList(listformat, items) -- Preview message and category local parameterMsg = require('Module:If preview')._warning({ 'Unknown parameter "_VALUE_".' }) .. "[[Category:Pages using vgrelease with named parameters|_VALUE_]]" -- Check for invalid parameters for k, v in pairs(args) do if (type(k) ~= 'number' and knownargs[k] ~= true) then local msg = parameterMsg:gsub('_VALUE_', k) out = out .. msg end end return out end return p 5f7f8d3ab03e85327a71b5355a3387ed5df65e4f Template:Original research 10 464 929 928 2023-07-10T15:58:18Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Original_research]] wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{Ambox | name = Original research | subst = <includeonly>{{subst:substcheck}}</includeonly> | type = content | class = ambox-Original_research | small = {{{small|}}} | issue = This {{{part|{{{1|article}}}}}} '''possibly contains [[Wikipedia:No original research|original research]]'''. {{#if:{{{reason|}}}|{{{reason|}}}}} | fix = Please [{{fullurl:{{FULLPAGENAME}}|action=edit}} improve it] by [[WP:Verifiability|verifying]] the claims made and adding [[Wikipedia:Citing sources#Inline citations|inline citations]]. Statements consisting only of original research should be removed. | removalnotice = yes | talk = {{{discuss|}}} | cat = Articles that may contain original research | all = All articles that may contain original research | date = {{{date|}}} }} }}<noinclude> {{Documentation}} </noinclude> c6ee867ff0fb8652b09b606114fadaa57737aeb4 Template:Plural 10 465 931 930 2023-07-10T15:58:18Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Plural]] wikitext text/x-wiki {{{1}}}{{{{{|safesubst:}}}#if:{{{{{|safesubst:}}}yesno|{{{nb|no}}}|no=}}|&nbsp;|&#32;}}{{{{{|safesubst:}}}plural:{{{1}}}|{{{2}}}|{{{3|{{{2}}}s}}}|{{{4}}}}}<noinclude> {{documentation}} <!-- Add categories to the /doc subpage; interwikis go to Wikidata, thank you! --> </noinclude> 89e87ed3de0186b2bfa1e9c61eb190eae2d72cb4 Template:Loop 10 466 933 932 2023-07-10T15:58:18Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Loop]] wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:String|rep|1={{{2|}}}|2={{{1|<noinclude>1</noinclude>}}}}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go in Wikidata --> </noinclude> bbbcfd80f9d6f4a62d5861134ad1d6f2d34ff82c Template:Rating 10 467 935 934 2023-07-10T15:58:19Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Rating]] wikitext text/x-wiki {{#if:{{{1|}}} |<span role="img" style="white-space:nowrap" title="{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}">{{#ifexpr:{{{1}}}>=1 |[[File:{{{full|Star full.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}|alt=]]{{Loop |{{#expr:floor{{{1}}}-1}} |[[File:{{{full|Star full.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|alt=]] }} |{{#ifexpr:{{{1}}}>0 |[[File:{{{half|Star half.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}|alt=]] |[[File:{{{empty|Star empty.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|{{{score|{{#if:{{{2|}}}|{{{1}}}/}}{{plural|{{#if:{{{2|}}}|{{{2}}}|{{{1}}}}}|{{{rating|star}}}|{{{rating-plural|{{{rating|star}}}s}}}}}}}}|alt=]] }} }}{{#ifexpr:{{{1}}}>1and{{{1}}}<>ceil{{{1}}} |[[File:{{{half|Star half.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|alt=]] }}{{#ifexpr:{{{2|-1}}}>ceil{{{1}}} |{{Loop |{{#expr:{{{2}}}-ceil{{{1}}}{{#ifexpr:{{{1}}}=0|-1}}}} |[[File:{{{empty|Star empty.svg}}}|{{{size|{{#ifexpr:{{{2|{{{1}}}}}}>6|7|11}}px}}}|link=|alt=]] }} }}</span>{{#ifexpr:{{{1}}}-floor{{{1}}}<>0and{{{1}}}-floor{{{1}}}<>.5 |{{main other|[[Category:Pages with incorrect use of Rating template]]}} }} |<span class="error">Please specify a '''rating'''.</span>{{main other|[[Category:Pages with incorrect use of Rating template]]}} }}<noinclude> {{documentation}} </noinclude> 5298598d04bb7ff507691d8d8d08034af42c87f1 Template:Video game reviews 10 468 937 936 2023-07-10T15:58:19Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Video_game_reviews]] wikitext text/x-wiki {{#invoke:Video game reviews|reviewbox }}<noinclude>{{template doc}}<!-- Add categories to the /doc sub-page and interwikis to Wikidata. --></noinclude> 4c0bdd60be06cf8fbc5260b0796615a76b14a075 Module:Video game reviews 828 469 939 938 2023-07-10T15:58:20Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Video_game_reviews]] Scribunto text/plain require('strict') local p = {} local data = require('Module:Video game reviews/data') local yesno = require('Module:Yesno') local vgwd = require('Module:Video game wikidata') local getArgs local function getActiveSystems(args) local activeSystems = {} for k, v in pairs(args) do if data.systems[k] and yesno(v) then table.insert(activeSystems, k) end end table.sort(activeSystems, function(a, b) return data.systems[a].sortkey < data.systems[b].sortkey end) return activeSystems end local function getArgKeyTables(args) local reviewers, aggregators, awards = {}, {}, {} for k in pairs(args) do if string.match(k, data.i18n.pattern.reviewer) then table.insert(reviewers, k) elseif string.match(k, data.i18n.pattern.aggregator) then table.insert(aggregators, k) elseif string.match(k, data.i18n.pattern.award) then table.insert(awards, k) end end local function comparator(a, b) return tonumber(a:match('%d+')) < tonumber(b:match('%d+')) end table.sort(reviewers, comparator) table.sort(aggregators, comparator) table.sort(awards, comparator) return reviewers, aggregators, awards end local function getProvidedReviewersAndAggregators(args, usePlatforms) local providedReviewers, providedAggregators = {}, {} if usePlatforms then local seen = {} for k in pairs(args) do local splitPos = string.find(k, '_') if splitPos then local halfarg = string.sub(k, 1, splitPos - 1) if not seen[halfarg] then seen[halfarg] = true if data.reviewers[halfarg] then table.insert(providedReviewers, halfarg) elseif data.aggregators[halfarg] then table.insert(providedAggregators, halfarg) end end end end else for k in pairs(args) do if not string.find(k, '_') then if data.reviewers[k] then table.insert(providedReviewers, k) elseif data.aggregators[k] then table.insert(providedAggregators, k) end end end end table.sort(providedReviewers, function(a, b) return data.reviewers[a].sortkey < data.reviewers[b].sortkey end) table.sort(providedAggregators, function(a, b) return data.aggregators[a].sortkey < data.aggregators[b].sortkey end) return providedReviewers, providedAggregators end local function renderHeadingRowWithSystems(builder, activeSystems, headingText) builder:tag('tr') :addClass(data.i18n.class.headerrow) :tag('th') :attr('scope', 'col') :attr('rowspan', '2') :wikitext(headingText) :done() :tag('th') :attr('scope', 'colgroup') :attr('colspan', #activeSystems) :wikitext(data.i18n.display.score) :done() builder = builder:tag('tr') for _, v in ipairs(activeSystems) do builder:tag('th') :wikitext(data.systems[v].name) :attr('scope', 'col') :done() end end local function renderHeadingRow(builder, nameHeading) builder:tag('tr') :addClass(data.i18n.class.headerrow) :tag('th') :attr('scope', 'col') :wikitext(nameHeading) :done() :tag('th') :attr('scope', 'col') :wikitext(data.i18n.display.score) :done() end local function renderRatingsBySystem(builder, code, name, activeSystems, args, na) builder = builder:tag('tr') builder:tag('td') :wikitext(name) for _, v in ipairs(activeSystems) do local combinedCode = code .. '_' .. v local cell = builder:tag('td') if args[combinedCode] then cell :wikitext(args[combinedCode]) :done() elseif na then cell :addClass(data.i18n.class.na) :wikitext(data.i18n.display.na) :done() end end end local function renderRating(builder, name, rating) builder:tag('tr') :tag('td') :addClass(data.i18n.class.centeredpub) :wikitext(name) :done() :tag('td') :wikitext(rating) :done() end local function renderAggregators(builder, providedAggregators, activeSystems, customAggregatorKeys, args) local aggregatorCount = #providedAggregators + #customAggregatorKeys if aggregatorCount == 0 then return end builder = builder:tag('table') :addClass(data.i18n.class.aggregators) :addClass(data.i18n.class.wikitable) :addClass(args.state and 'mw-collapsible-content' or nil) :tag('caption') :wikitext(data.i18n.display[aggregatorCount == 1 and 'aggregateScore' or 'aggregateScores']) :done() if #activeSystems ~= 0 then local na = yesno(args.na) local showplatforms = #activeSystems ~= 1 or yesno(args.showplatforms) if showplatforms then renderHeadingRowWithSystems(builder, activeSystems, data.i18n.display.aggregator) else renderHeadingRow(builder, data.i18n.display.aggregator) end for _, v in ipairs(providedAggregators) do renderRatingsBySystem(builder, v, data.aggregators[v].name, activeSystems, args, na) end for _, v in ipairs(customAggregatorKeys) do renderRatingsBySystem(builder, v, args[v], activeSystems, args, na) end else renderHeadingRow(builder, data.i18n.display.aggregator) for _, v in ipairs(providedAggregators) do renderRating(builder, data.aggregators[v].name, args[v]) end for _, v in ipairs(customAggregatorKeys) do renderRating(builder, args[v], args[v .. 'Score']) end end end local function renderReviews(builder, providedReviewers, activeSystems, customReviewerKeys, args, reviewerCount, priorReviewCount) if reviewerCount == 0 then return end builder = builder:tag('table') :addClass(data.i18n.class.reviews) :addClass(data.i18n.class.wikitable) :addClass(args.state and 'mw-collapsible-content' or nil) :tag('caption') :wikitext(data.i18n.display[reviewerCount == 1 and 'reviewScore' or 'reviewScores']) :addClass(priorReviewCount > 0 and data.i18n.class.stacked or nil) :done() if #activeSystems ~= 0 then local na = yesno(args.na) local showplatforms = #activeSystems ~= 1 or yesno(args.showplatforms) if showplatforms then renderHeadingRowWithSystems(builder, activeSystems, data.i18n.display.publication) else renderHeadingRow(builder, data.i18n.display.publication) end for _, v in ipairs(providedReviewers) do renderRatingsBySystem(builder, v, data.reviewers[v].name, activeSystems, args, na) end for _, v in ipairs(customReviewerKeys) do renderRatingsBySystem(builder, v, args[v], activeSystems, args, na) end else renderHeadingRow(builder, data.i18n.display.publication) for _, v in ipairs(providedReviewers) do renderRating(builder, data.reviewers[v].name, args[v]) end for _, v in ipairs(customReviewerKeys) do renderRating(builder, args[v], args[v .. 'Score']) end end end local function renderAwards(builder, args, awardKeys, priorReviewCount) if #awardKeys == 0 then return end builder = builder:tag('table') :addClass(data.i18n.class.awards) :addClass(data.i18n.class.wikitable) :addClass(args.state and 'mw-collapsible-content' or nil) :tag('caption') :wikitext(data.i18n.display[#awardKeys == 1 and 'award' or 'awards']) :addClass(priorReviewCount > 0 and data.i18n.class.stacked or nil) :done() :tag('tr') :tag('th') :attr('scope', 'col') :wikitext(data.i18n.display.publication) :done() :tag('th') :attr('scope', 'col') :wikitext(data.i18n.display.award) :done() for _, v in ipairs(awardKeys) do builder:tag('tr') :tag('td') :wikitext(args[v .. 'Pub']) :done() :tag('td') :wikitext(args[v]) :done() end builder:done() builder:done() end local function renderEditOnWikidata(builder, wikidata, state) if not wikidata then return end builder:tag('div') :addClass(data.i18n.class.wikidata) :addClass(state and 'mw-collapsible-content' or nil) :wikitext(vgwd.getUpdateLink()) :done() end local function categorizePlatformCount(builder, platformCount) if platformCount ~= 0 then builder:wikitext(data.i18n.category.multiplatform) else builder:wikitext(data.i18n.category.singleplatform) end end local function renderTitles(builder, title, subtitle) builder:tag('div') :addClass(data.i18n.class.title) :wikitext(title or data.i18n.display.reception) :done() if subtitle then builder:tag('div') :addClass(data.i18n.class.subtitle) -- The only reason to use the subtitle is collapsible content -- So always add the related class. :addClass('mw-collapsible-content') :wikitext(subtitle) :done() end end local function render(providedReviewers, providedAggregators, awardKeys, activeSystems, customAggregatorKeys, customReviewerKeys, args, wikidata) local is_collapsible = args.title and args.state and (args.state == data.i18n.state.autocollapse or args.state == data.i18n.state.collapsed or args.state == data.i18n.state.expanded ) local div = mw.html.create('div') :attr('role', 'complementary') :addClass(data.i18n.class.container) :addClass(#activeSystems == 0 and data.i18n.class.containersingle or nil) :addClass(args.align == data.i18n.align.left and data.i18n.class.containerleft or nil) :addClass(args.align == data.i18n.align.none and data.i18n.class.containernone or nil) :addClass(is_collapsible and 'mw-collapsible' or nil) :addClass(is_collapsible and args.state == data.i18n.state.collapsed and 'mw-collapsed' or nil) :addClass(is_collapsible and args.state == data.i18n.state.autocollapse and args.state or nil) renderTitles(div, args.title, args.subtitle) local aggregatorCount = #providedAggregators + #customAggregatorKeys renderAggregators( div, providedAggregators, activeSystems, customAggregatorKeys, args, aggregatorCount ) local reviewerCount = #customReviewerKeys + #providedReviewers renderReviews( div, providedReviewers, activeSystems, customReviewerKeys, args, reviewerCount, aggregatorCount ) renderAwards( div, args, awardKeys, reviewerCount + aggregatorCount ) renderEditOnWikidata(div, wikidata, args.state) categorizePlatformCount(div, #activeSystems) return div end local function checkForWikidata(frame, args, activeSystems, providedAggregators) local wikidata = false if args.qid == 'none' then return wikidata end vgwd.setDateFormat(args.df) vgwd.setGame(args.qid) vgwd.setSystem(nil) vgwd.setGenerateReferences(true) vgwd.setShowUpdateLink(false) vgwd.setUpdateLinkStyle("text and pen") vgwd.setSystemFormat(args.systemFormat) -- Loop through aggregators if we have any. if #providedAggregators ~= 0 then for _, aggr in ipairs(providedAggregators) do -- Check if vgwd knows this aggregator. if vgwd.setReviewer(aggr) == nil then -- Loop through active systems if #activeSystems ~= 0 then for _, sys in ipairs(activeSystems) do local combinedCode = aggr .. '_' .. sys if args[combinedCode] == 'wikidata' then vgwd.setSystem(sys) vgwd.setShowSystem(false) local vgwdScore = vgwd.printReviewScores(frame) if vgwdScore then args[combinedCode] = vgwdScore end wikidata = true end end else vgwd.setShowSystem(true) if args[aggr] == 'wikidata' then local vgwdScore = vgwd.printReviewScores(frame) if vgwdScore then args[aggr] = vgwdScore end wikidata = true end end end end end return wikidata end function p._reviewbox(frame, args) local activeSystems = getActiveSystems(args) local customReviewerKeys, customAggregatorKeys, awardKeys = getArgKeyTables(args) local providedReviewers, providedAggregators = getProvidedReviewersAndAggregators(args, #activeSystems ~= 0) local wikidata = checkForWikidata(frame, args, activeSystems, providedAggregators) if #customAggregatorKeys ~= 0 or #customReviewerKeys ~= 0 or #providedAggregators ~= 0 or #providedReviewers ~= 0 or #awardKeys ~= 0 then return frame:extensionTag{ name='templatestyles', args = { src = data.i18n.templatestyles } } .. tostring(render( providedReviewers, providedAggregators, awardKeys, activeSystems, customAggregatorKeys, customReviewerKeys, args, wikidata )) elseif mw.title.getCurrentTitle().namespace == 0 then return data.i18n.category.empty end end function p.reviewbox(frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end return p._reviewbox(frame, getArgs(frame, { wrappers = data.i18n.wrapper, trim = false, translate = data.argi18n } )) end return p 29883de6139a02f8c525496cd6f849740015b6b9 Module:Video game reviews/data 828 470 941 940 2023-07-10T15:58:20Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Video_game_reviews/data]] Scribunto text/plain local reviewers = { { "''[[1Up.com]]''", '1UP' }, { "''[[4Players]]''", '4P' }, { "''[[ACE (magazine)|ACE]]''", 'ACE' }, { "''[[Adventure Gamers]]''", 'AdvGamers' }, { "''[[AllGame]]''", 'Allgame' }, { "''[[Aktueller Software Markt]]''", 'ASM' }, { "''[[Amiga Action]]''", 'AmAction' }, { "''[[Amiga Computing]]''", 'AmComputing' }, { "''[[Amiga Force]]''", 'AmForce' }, { "''[[Amiga Format]]''", 'AmFormat' }, { "''[[Amiga Power]]''", 'AmPower' }, { "''[[Amiga User International]]''", 'AmUI' }, { "''[[Amstrad Action]]''", 'AAction' }, { "''[[Amtix]]''", 'Amtix' }, { "''[[The A.V. Club]]''", 'AVC' }, { "''[[Gemaga|Beep! MegaDrive]]''", 'BMD' }, { "''[[CNET Gamecenter]]''", 'CNG' }, { "''[[Computer Game Review]]''", 'CGR' }, { "''[[Computer Games Magazine]]''", 'CGM' }, { "''[[Computer Games Magazine|Computer Games Strategy Plus]]''", 'CGSP' }, { "''[[Computer Gaming World]]''", 'CGW' }, { "''Consoles +''", 'CP' }, { "''[[Crash (magazine)|Crash]]''", 'CRASH' }, { "''[[Computer and Video Games]]''", 'CVG' }, { "''[[Destructoid]]''", 'Destruct' }, { "''[[Digital Trends]]''", 'DT' }, { "''[[Dragon (magazine)|Dragon]]''", 'Dragon' }, { "''[[Easy Allies]]''", 'EZA' }, { "''[[Edge (magazine)|Edge]]''", 'Edge' }, { "''[[Electronic Gaming Monthly]]''", 'EGM' }, { "''[[EP Daily]]''", 'EPD' }, { "''[[Eurogamer]]''", 'EuroG' }, { "''[[Famitsu]]''", 'Fam' }, { "''[[G4 (American TV network)|G4]]''", 'G4' }, { "''[[Game Informer]]''", 'GI' }, { "''[[Game Players]]''", 'GP' }, { "''[[GameDaily]]''", 'GD' }, { "''[[GameFan]]''", 'GameFan' }, { "''[[Gamekult]]''", 'Gamekult' }, { "''[[GamePro]]''", 'GamePro' }, { "''[[GameRevolution]]''", 'GameRev' }, { "''[[GamesMaster (magazine)|GamesMaster]]''", 'GMaster' }, { "''[[GameSpot]]''", 'GSpot' }, { "''[[GameSpy]]''", 'GSpy' }, { "''[[GamesRadar+]]''", 'GRadar' }, { "''[[GameStar]]''", 'GStar' }, { "''[[GamesTM]]''", 'GTM' }, { "''[[Games-X]]''", 'GX' }, { "''[[GameTrailers]]''", 'GT' }, { "''[[Gamezebo]]''", 'Gamezebo' }, { "''GameZone''", 'GameZone' }, { "''Gekkan PC Engine''", 'GPCE' }, { "''Génération 4''", 'Gen4' }, { "''[[Giant Bomb]]''", 'GB' }, { "''Hardcore Gamer''", 'HCG' }, { "''[[HobbyConsolas]]''", 'HC' }, { "''[[Hyper (magazine)|Hyper]]''", 'Hyper' }, { "''[[IGN]]''", 'IGN' }, { "''Impress Watch''", 'IW' }, { "''[[Jeuxvideo.com]]''", 'JXV' }, { "''Joypad''", 'JP' }, { "''[[Joystick (magazine)|Joystick]]''", 'JS' }, { "''[[Joystiq]]''", 'Joystiq' }, { "''[[Kill Screen]]''", 'KS' }, { "''M! Games''", 'MG' }, { "''[[MacLife]]''", 'ML' }, { "''[[Macworld]]''", 'MW' }, { "''Marukatsu PC Engine''", 'MPCE' }, { "''[[Maximum PC]]''", 'MaxPC' }, { "''[[Mean Machines Sega]]''", 'MMS' }, { "''Mega Fun''", 'MF' }, { "''MeriStation''", 'MS' }, { "''[[Micromanía]]''", 'MIC' }, { "''[[NGC Magazine|N64 Magazine]]''", 'N64' }, { "''[[Next Generation (magazine)|Next Generation]]''", 'NGen' }, { "''[[Nintendo Gamer|NGamer]]''", 'NG' }, { "''[[NGC Magazine]]''", 'NGC' }, { "''[[Nintendo Life]]''", 'NLife' }, { "''[[Nintendo Power]]''", 'NP' }, { "''Nintendo World Report''", 'NWR' }, { "''[[NME]]''", 'NME' }, { "''[[Official Nintendo Magazine]]''", 'ONM' }, { "''[[PlayStation Official Magazine – Australia]]''", 'OPMAU' }, { "''[[PlayStation Official Magazine – UK]]''", 'OPMUK' }, { "''[[Official U.S. PlayStation Magazine]]''", 'OPM' }, { "[[Official Xbox Magazine|''Official Xbox Magazine'' (UK)]]", 'OXMUK' }, { "[[Official Xbox Magazine|''Official Xbox Magazine'' (US)]]", 'OXM' }, { "''PALGN''", 'PALGN' }, { "''[[PC Accelerator]]''", 'PCA' }, { "''PC Engine Fan''", 'PCEF' }, { "''[[PC Format]]''", 'PCF' }, { "[[PC Gamer|''PC Gamer'' (UK)]]", 'PCGUK' }, { "[[PC Gamer|''PC Gamer'' (US)]]", 'PCGUS' }, { "[[PC Games|''PC Games'' (DE)]]", 'PCG' }, { "[[GamePro#PC Games|''PC Games'' (US)]]", 'GPPCG' }, { "''[[PC PowerPlay]]''", 'PCPP' }, { "''[[PC Zone]]''", 'PCZone' }, { "''[[PCGamesN]]''", 'PCGN' }, { "''[[PCMag]]''", 'PCM' }, { "''[[Play (UK magazine)|Play]]''", 'Play' }, { "''[[Pocket Gamer]]''", 'PG' }, { "''[[Polygon (website)|Polygon]]''", 'Poly' }, { "''Player One''", 'PO' }, { "''[[PlayStation: The Official Magazine]]''", 'PSM' }, { "''[[PSM3]]''", 'PSM3' }, { "''[[Push Square]]''", 'PSQ' }, { "''[[Newsfield#Raze|Raze]]''", 'Raze' }, { "''[[Retro Gamer]]''", 'Retro' }, { "''RPGamer''", 'RPG' }, { "''RPGFan''", 'RPGFan' }, { "''[[Shacknews]]''", 'SN' }, { "''[[Sinclair User]]''", 'SUser' }, { "''[[ST Action]]''", 'STAction' }, { "''[[ST Format]]''", 'STFormat' }, { "''[[ST Review]]''", 'STRev' }, { "''Superjuegos''", 'SJ' }, { "''Super Game Power''", 'SGP' }, { "''[[Super Play]]''", 'SP' }, { "''[[TeamXbox]]''", 'TX' }, { "''[[TechRadar]]''", 'TR' }, { "''[[The Daily Telegraph|The Telegraph]]''", 'TELE' }, { "[[The Games Machine|''The Games Machine'' (UK)]]", 'TGM' }, { "[[The Games Machine (Italy)|''The Games Machine'' (Italy)]]", 'TGMIt' }, { "''[[The Guardian]]''", 'TG' }, { "''[[Tilt (French magazine)|Tilt]]''", 'TILT' }, { "''[[Total!]]''", 'TOT' }, { "''[[TouchArcade]]''", 'TA' }, { "''[[USgamer]]''", 'USG' }, { "''[[VentureBeat]]''", 'VB' }, { "''[[Video Games Chronicle]]''", 'VGC' }, { "''[[VG247]]''", 'VG247' }, { "''Video Games'' (DE)", 'VGS' }, { "''VideoGamer.com''", 'VG' }, { "''[[VideoGames & Computer Entertainment]]''", 'VGCE' }, { "''[[X-Play]]''", 'XPlay' }, { "''[[Your Sinclair]]''", 'YSinclair' }, { "''[[Zero (video game magazine)|Zero]]''", 'Zero' }, { "''[[Zzap!64]]''", 'Z64' }, } local aggregators = { { '[[GameRankings]]', 'GR' }, { '[[Metacritic]]', 'MC' }, { '[[OpenCritic]]', 'OC' }, } local systems = { { '[[3DO Interactive Multiplayer|3DO]]', '3DO' }, { '[[Nintendo 3DS|3DS]]', '3DS' }, { '[[Amiga]]', 'AMI' }, { '[[Arcade game|Arcade]]', 'ARC' }, { '[[Atari 2600]]', 'A2600' }, { '[[Atari Jaguar]]', 'JAG' }, { '[[Atari Lynx]]', 'LYNX' }, { '[[Atari ST]]', 'AST' }, { '[[Commodore 64|C64]]', 'C64' }, { '[[Amiga CD32|CD32]]', 'CD32' }, { '[[ColecoVision]]', 'CV' }, { '[[MS-DOS|DOS]]', 'DOS' }, { '[[Dreamcast]]', 'SDC' }, { '[[Nintendo DS|DS]]', 'DS' }, { '[[Game Boy]]', 'GB' }, { '[[Game Boy Advance|GBA]]', 'GBA' }, { '[[Game Boy Color|GBC]]', 'GBC' }, { '[[GameCube|GC]]', 'NGC' }, { '[[List of video game consoles|General]]', 'GEN' }, { '[[Intellivision]]', 'INT' }, { '[[iOS]]', 'iOS' }, { '[[Macintosh]]', 'MAC' }, { '[[Master System]]', 'SMS' }, { '[[Mobile phone|mobile]]', 'MOB' }, { '[[N-Gage (device)|N-Gage]]', 'N-G' }, { '[[Nintendo 64|N64]]', 'N64' }, { '[[Nintendo Entertainment System|NES]]', 'NES' }, { '[[Nintendo Switch|NS]]', 'NS' }, { '[[Personal computer|PC]]', 'PC' }, { '[[PlayStation Vita|PS Vita]]', 'VITA' }, { '[[PlayStation (console)|PS]]', 'PS' }, { '[[PlayStation 2|PS2]]', 'PS2' }, { '[[PlayStation 3|PS3]]', 'PS3' }, { '[[PlayStation 4|PS4]]', 'PS4' }, { '[[PlayStation 5|PS5]]', 'PS5' }, { '[[PlayStation Portable|PSP]]', 'PSP' }, { '[[Sega Saturn|Saturn]]', 'SSAT' }, { '[[Sega Genesis]]', 'SMD' }, { '[[Sega Game Gear|SGG]]', 'SGG' }, { '[[Nvidia Shield|Shield]]', 'NSHI' }, { '[[Super Nintendo Entertainment System|SNES]]', 'SNES' }, { '[[TurboGrafx-16]]', 'TG16' }, { '[[Wii]]', 'WII' }, { '[[Wii U]]', 'WIIU' }, { '[[Xbox (console)|Xbox]]', 'XBOX' }, { '[[Xbox 360]]', 'X360' }, { '[[Xbox One]]', 'XONE' }, { '[[Xbox Series X and Series S|Xbox Series X/S]]', 'XSXS' }, { '[[ZX Spectrum|ZX]]', 'ZX' }, } local function setupSortkeys(t) local retval = {} for k, v in ipairs(t) do retval[v[2]] = { name = v[1], sortkey = k } end return retval end return { reviewers = setupSortkeys(reviewers), aggregators = setupSortkeys(aggregators), systems = setupSortkeys(systems), i18n = { wrapper = 'Template:Video game reviews', templatestyles = 'Module:Video game reviews/styles.css', pattern = { reviewer = '^rev%d+$', aggregator = '^agg%d+$', award = '^award%d+$', }, class = { aggregators = 'vgr-aggregators', awards = 'vgr-awards', centeredpub = 'vgr-center', container = 'video-game-reviews', containerleft = 'vgr-left', containernone = 'vgr-none', containersingle = 'vgr-single', headerrow = 'vgr-hrow', na = 'table-na', -- same as Template:n/a reviews = 'vgr-reviews', stacked = 'vgr-stacked', subtitle = 'vgr-subtitle', title = 'vgr-title', wikidata = 'vgr-edit-on-wikidata', -- we keep wikitable around even though all the styles are overriden -- because it helps some scripts to know to look for a real data table wikitable = 'wikitable', }, state = { autocollapse = 'autocollapse', -- you may not have this on your wiki collapsed = 'collapsed', expanded = 'expanded', }, align = { left = 'left', none = 'none', }, category = { empty = '[[Category:Empty templates on articles]]', multiplatform = '[[Category:Articles using Video game reviews template in multiple platform mode]]', singleplatform = '[[Category:Articles using Video game reviews template in single platform mode]]', }, display = { aggregateScore = 'Aggregate score', aggregateScores = 'Aggregate scores', aggregator = 'Aggregator', award = 'Award', awards = 'Awards', na = 'N/A', publication = 'Publication', reception = 'Reception', reviewScore = 'Review score', reviewScores = 'Review scores', score = 'Score', }, }, argi18n = { -- For non-English wikis, add translations of argument keys here. -- Example: -- subtitle = 'untertitel' }, } 83ed24f7e9467631a402d0d410b9e45b579afad3 Module:Video game reviews/styles.css 828 471 943 942 2023-07-10T15:58:20Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Video_game_reviews/styles.css]] text text/plain /* {{pp|small=yes}} */ .video-game-reviews { float: right; clear: right; margin: 0 1em 1em; text-align: center; padding: 0; } /* As with width below, .vgr-left and .vgr-none maybe shouldn't exist. Someone who really needs this to be left rather than right can always plop it in its own div... */ .vgr-left { float: left; clear: left; margin: 0 1em 1em 0; } .vgr-none { float: none; clear: left; margin: 0 1em 1em 0; } .vgr-single { width: 23em; } .vgr-edit-on-wikidata { border: 1px solid #a2a9b1; border-top: none; padding: 0.2em; /* Revisit background color because Wikidata link is only about 4.0 contrast. Something for the talk page. */ background: #d1dbdf; font-size: 88%; font-weight: bold; } .video-game-reviews table { border: 1px solid #a2a9b1; /* @noflip */ margin: 0; font-size: 88%; width: 100%; } .video-game-reviews td, .video-game-reviews th, .video-game-reviews caption { border: 1px solid #a2a9b1; vertical-align: middle; } .video-game-reviews caption { border-bottom: none; background: #d1dbdf; text-align: center; padding: 0.2em 0.4em; } .video-game-reviews th { background: #eaecf0; } .vgr-awards td { background: #f2f2f2; } .vgr-hrow th { background: #e8f4f8; } .video-game-reviews .table-na { color: #707070 } .vgr-reviews, .vgr-reviews tr:last-child td, .vgr-reviews tr:last-child th { border-bottom: none; } .vgr-title, .vgr-subtitle, .vgr-awards tr td:first-child { font-weight: bold; } .mw-collapsed .vgr-title { display: inline; /* so that it doesn't "wrap" to the next line */ } .video-game-reviews table tr td:first-child, .vgr-awards td { text-align: left; } .video-game-reviews table tr td.vgr-center { /* Needed to override text-align left above in one particular case where it looks prettier. Usually it would make more sense to make a semantic name */ text-align: center; } .video-game-reviews .vgr-stacked { border-top: none; } @media (max-width: 720px) { .video-game-reviews { width: 100%; float: none; /* set float/clear explicitly to override earlier */ clear: both; margin: 0; } .video-game-reviews table { display: table; /* Minerva is silly */ } .video-game-reviews caption { display: table-caption; /* Minerva is still silly */ } } 638393c71fab9b2fc8e15113df813beecb4d842e Module:Video game wikidata 828 472 945 944 2023-07-10T15:58:21Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Video_game_wikidata]] Scribunto text/plain local Date = require('Module:Date')._Date local yesno = require('Module:Yesno') local p = {} -- Local variables. local reviewer = nil; local df = "mdy"; local entity = nil; local genRefs = true; local showSystem = true; local showUpdateLink = true; local system = nil; local systemId = nil; local systemFormat = "colon"; local updateLinkStyle = nil; local entities = {}; -- Translation table for converting numeric-IDs to shorthand aliases. local systemAliases = { [10677] = 'PS1', [1323662] = 'PS1', -- Placeholder, this is actually the series but could be mistakenly used for PS1. [10680] = 'PS2', [10683] = 'PS3', [5014725] = 'PS4', [16338] = 'PC', [8079] = 'Wii', [56942] = 'WiiU', [132020] = 'XBOX', [48263] = 'X360', [13361286] = 'XONE', [203597] = '3DS', [188808] = 'PSV', [170323] = 'DS', -- Sometimes has been NDS [170325] = 'PSP', [48493] = 'IOS', -- iOS, iPhone, iPad [94] = 'AND', -- Android [186437] = 'GB', [188642] = 'GBA', [203992] = 'GBC', [184198] = 'DC', [200912] = 'SAT', [172742] = 'NES', [183259] = 'SNES', [184839] = 'N64', [182172] = 'GC', -- Sometimes has been NGC [19610114] = 'NS', -- Nintendo Switch [98973368] = 'XSXS', -- Xbox Series X and Series S [63184502] = 'PS5' } -- Translation table for converting system aliases to QIDs local systemIDs = { ['PS1'] = 10677, ['PS2'] = 10680, ['PS3'] = 10683, ['PS4'] = 5014725, ['PC'] = 16338, ['WII'] = 8079, ['WIIU'] = 56942, ['XBOX'] = 132020, ['X360'] = 48263, ['XONE'] = 13361286, ['3DS'] = 203597, ['PSV'] = 188808, ['DS'] = 170323, ['NDS'] = 170323, ['PSP'] = 170325, ['IOS'] = 48493, ['AND'] = 94, ['GB'] = 186437, ['GBA'] = 188642, ['GBC'] = 203992, ['DC'] = 184198, ['SAT'] = 200912, ['NES'] = 172742, ['SNES'] = 183259, ['N64'] = 184839, ['GC'] = 182172, ['NGC'] = 182172, ['NS'] = 19610114, ['XSXS'] = 98973368, ['PS5'] = 63184502 } -- List of accepted aggregator arguments and their related QID. local aggregatorAliases = { [150248] = 'MC', [40160] = 'GR', [21039459] = 'OC' } -- List of accepted aggregator arguments and their related QID. local aggregatorIDs = { ['MC'] = 150248, ['GR'] = 40160, ['OC'] = 21039459 } -- List of accepted reviewer arguments and their related QID. local reviewerAliases = { [591573] = 'FAM', [207708] = 'IGN' } -- List of accepted reviewer arguments and their related QID. local reviewerIDs = { ['FAM'] = 591573, ['IGN'] = 207708 } local function sortByPlatform(a,b) local platformA = ""; local platformB = ""; if(a['qualifiers']['P400'] ~= nil and a['qualifiers']['P400'][1] ~= nil) then platformA = p.getSystemAlias(a['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); if(platformA == nil) then platformA = mw.wikibase.label('Q'..a['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); end; end; if(b['qualifiers']['P400'] ~= nil and b['qualifiers']['P400'][1] ~= nil) then platformB = p.getSystemAlias(b['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); if(platformB == nil) then platformB = mw.wikibase.label('Q'..b['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); end; end; return platformA < platformB end; local function buildCite(reference) local referenceUrl = nil; local cite = nil; if(reference['snaks']['P854'] ~= nil and reference['snaks']['P854'][1] ~= nil) then referenceUrl = reference['snaks']['P854'][1]['datavalue']['value']; end; if(referenceUrl ~= nil and referenceUrl ~= "") then cite = "{{cite web|url="..referenceUrl; local pubdate = nil; local accessdate = nil; local publisher = nil; local work = nil; local title = nil; local archiveUrl = nil; local archiveDate = nil; local authors = {}; if(reference['snaks']['P577'] ~= nil and reference['snaks']['P577'][1] ~= nil) then pubdate = reference['snaks']['P577'][1]['datavalue']['value']['time']; end; if(reference['snaks']['P813'] ~= nil and reference['snaks']['P813'][1] ~= nil) then accessdate = reference['snaks']['P813'][1]['datavalue']['value']['time']; end; if(reference['snaks']['P123'] ~= nil and reference['snaks']['P123'][1] ~= nil) then publisher = mw.wikibase.label('Q'..reference['snaks']['P123'][1]['datavalue']['value']['numeric-id']); end; if(reference['snaks']['P1433'] ~= nil and reference['snaks']['P1433'][1] ~= nil) then work = mw.wikibase.label('Q'..reference['snaks']['P1433'][1]['datavalue']['value']['numeric-id']); end; if(reference['snaks']['P1476'] ~= nil and reference['snaks']['P1476'][1] ~= nil) then title = reference['snaks']['P1476'][1]['datavalue']['value']['text']; end; if(reference['snaks']['P1065'] ~= nil and reference['snaks']['P1065'][1] ~= nil) then archiveUrl = reference['snaks']['P1065'][1]['datavalue']['value']; end; if(reference['snaks']['P2960'] ~= nil and reference['snaks']['P2960'][1] ~= nil) then archiveDate = reference['snaks']['P2960'][1]['datavalue']['value']['time']; end; if(reference['snaks']['P50'] ~= nil and #reference['snaks']['P50'] > 0) then for i,authorDat in pairs(reference['snaks']['P50']) do local authorQid = 'Q'..authorDat['datavalue']['value']['numeric-id']; if(entities[authorQid] == nil) then entities[authorQid] = mw.wikibase.getEntity(authorQid); end; local author = {}; author['fullname'] = mw.wikibase.label(authorQid); -- Default to label author['first'] = nil; author['last'] = nil; if(entities[authorQid]['claims']['P735'] ~= nil and entities[authorQid]['claims']['P735'][1] ~= nil) then author['first'] = mw.wikibase.label('Q'..entities[authorQid]['claims']['P735'][1]['mainsnak']['datavalue']['value']['numeric-id']); end; if(entities[authorQid]['claims']['P734'] ~= nil and entities[authorQid]['claims']['P734'][1] ~= nil) then author['last'] = mw.wikibase.label('Q'..entities[authorQid]['claims']['P734'][1]['mainsnak']['datavalue']['value']['numeric-id']); end; table.insert(authors, author); end; end; if(title ~= nil and title ~= "") then cite = cite .. "|title="..title; end; if(publisher ~= nil and publisher ~= "") then cite = cite .. "|publisher="..publisher; end; if(work ~= nil and work ~= "") then cite = cite .. "|work="..work; end; if(pubdate ~= nil and pubdate ~= "") then local pubdateText = Date(pubdate):text(df); cite = cite .. "|date="..pubdateText; end; if(accessdate ~= nil and accessdate ~= "") then local accessdateText = Date(accessdate):text(df); cite = cite .. "|accessdate="..accessdateText; end; if(archiveUrl ~= nil and archiveUrl ~= "" and archiveDate ~= nil and archiveDate ~= "") then local archivedateText = Date(archiveDate):text(df); cite = cite .. "|archiveurl="..archiveUrl; cite = cite .. "|archivedate="..archivedateText; end; if(#authors > 0) then for i,author in pairs(authors) do if(author['first'] ~= nil and author['last'] ~= nil and author['first'] ~= "" and author['last'] ~= "") then if(#authors == 1) then cite = cite .."|last="..author['last'].."|first="..author['first']; else cite = cite .."|last"..i.."="..author['last'].."|first"..i.."="..author['first']; end; else if(#authors == 1) then cite = cite .."|author="..author['fullname']; else cite = cite .."|author"..i.."="..author['fullname']; end; end; end; end; cite = cite..'}}'; end; return cite; end; local function printReviewRow(frame, reviewscore) local score = nil; if(reviewscore['mainsnak']['datavalue'] ~= nil and reviewscore['mainsnak']['datavalue']['value'] ~= nil) then score = reviewscore['mainsnak']['datavalue']['value']; else return ""; end; local ret = "" local system = nil; local reference = nil; if(reviewscore['qualifiers']['P400'] ~= nil and reviewscore['qualifiers']['P400'][1] ~= nil) then system = p.getSystemAlias(reviewscore['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']); end if(system ~= nil and system ~= "" and showSystem) then if(systemFormat == "para") then ret = ret.."("..system..") "; else ret = ret..system..": "; end; end; ret = ret..score; if(reviewscore['references'] ~= nil and reviewscore['references'][1] ~= nil and genRefs) then local cite = buildCite(reviewscore['references'][1]); if(cite ~= nil) then local scoreBy = p.getAggregatorAlias(reviewscore['qualifiers']['P447'][1]['datavalue']['value']['numeric-id']); if(scoreBy == nil) then scoreBy = p.getReviewerAlias(reviewscore['qualifiers']['P447'][1]['datavalue']['value']['numeric-id']); end; local name = entity:getLabel()..'-'..scoreBy; if(system ~= nil and system ~= "") then name = name..system; end; cite = frame:extensionTag{ name = "ref", args = {name=name}, content=cite }; ret = ret..cite; end; end; return ret.."<br />"; end function p.getSystemAlias(numericId) return systemAliases[numericId]; end function p.getSystemID(system) return systemIDs[system]; end function p.getAggregatorAlias(numericId) return aggregatorAliases[numericId]; end function p.getAggregatorID(system) return aggregatorIDs[system]; end function p.getReviewerAlias(numericId) return reviewerAliases[numericId]; end function p.getReviewerID(system) return reviewerIDs[system]; end function p.setReviewer(iReviewer) -- No reviewer, stop. Must have reviewer at least. if(iReviewer == nil or iReviewer == "") then return "Missing reviewer"; end; -- See if supplied reviewer is in the aggregator table. iReviewer = string.upper(iReviewer) reviewer = p.getAggregatorID(iReviewer); if(reviewer == nil or reviewer == "") then -- No? Maybe in the reviewer table. reviewer = p.getReviewerID(iReviewer); if(reviewer == nil or reviewer == "") then return "Invalid reviewer"; end; end; return nil; end; function p.setDateFormat(iDf) -- Check for a date format parameter. Default to mdy if missing. if(iDf ~= nil and iDf ~= "") then df = string.lower(iDf); end; end; function p.setSystemFormat(iSf) if(iSf ~= nil and iSf ~= "") then systemFormat = string.lower(iSf); end; end; function p.setUpdateLinkStyle(iStyle) if(iStyle ~= nil and iStyle ~= "") then updateLinkStyle = string.lower(iStyle); end; end; function p.setGame(iGame) -- Check for a game parameter. If missing, default to current article. if(iGame ~= nil and iGame ~= "") then if(entities[iGame] == nil and mw.wikibase ~= nil) then entities[iGame] = mw.wikibase.getEntity(iGame); end; entity = entities[iGame] else -- Need to research if we can determine the entity's ID before retrieving it. if(mw.wikibase ~= nil) then entity = mw.wikibase.getEntity(); if(entity ~= nil) then entities[entity['id']] = entity; end; end; end; if(entity == nil) then return "No matching wikidata entity found"; end; return nil; end; function p.setSystem(iSystem) -- Check for system parameter, and resolve it's QID if possible. if(iSystem ~= nil and iSystem ~= "") then system = string.upper(iSystem); systemId = p.getSystemID(system); elseif(not showSystem) then -- If no system was specified, force showSystem on. showSystem = true; end; end; function p.setGenerateReferences(iGenRefs) -- Reference suppression. if(iGenRefs ~= nil and iGenRefs ~= "") then genRefs = yesno(iGenRefs, true); end; end; function p.setShowSystem(iShowSystem) -- Suppression of system aliases in front of score, i.e. (XBOX) xx/100. if(iShowSystem ~= nil and iShowSystem ~= "") then showSystem = yesno(iShowSystem, false); end; if(system == nil or system == '') then -- If no system was specified, force showSystem on. showSystem = true; end; end; function p.setShowUpdateLink(iShowUpdateLink) -- Suppression of update link to Wikidata at the end of the score, i.e. (XBOX) xx/100[+]. if(iShowUpdateLink ~= nil and iShowUpdateLink ~= "") then showUpdateLink = yesno(iShowUpdateLink, false); end; end; function p.getUpdateLink() if(updateLinkStyle == "pen") then return "[[File:Blue pencil.svg|frameless|text-top|10px|alt=Edit this on Wikidata|link=https://www.wikidata.org/wiki/"..entity['id'].."?uselang="..mw.language.getContentLanguage().code.."#P444|Edit this on Wikidata]]"; elseif(updateLinkStyle == "noSub") then return '[[d:'..entity['id']..'#P444|&#91;±&#93;]]'; elseif(updateLinkStyle == "text and pen") then return '<span style="position: relative;"><span style="position: absolute; right: 0;">[[File:Blue pencil.svg|10px|baseline|link=|alt=]]</span>[[d:'..entity['id']..'#P444|<span style="position: relative; padding-right: 14px;">Edit on Wikidata</span>]]</span>' end; return '<sub>[[d:'..entity['id']..'#P444|&#91;±&#93;]]</sub>'; end; function p.getSitelink() return mw.wikibase.sitelink(entity['id']); end; function p.getLabel() return mw.wikibase.label(entity['id']); end; function p.getParts() local ret = {}; -- Loop all of "has Part" for this title local parts = entity['claims']['P527']; if(parts) then for i,part in pairs(parts) do table.insert(ret,"Q"..part['mainsnak']['datavalue']['value']['numeric-id']); end; end; return ret; end; function p.getEarliestPublicationDate() local ret = {}; local pubDates = entity['claims']['P577']; if(pubDates) then for i,pubDate in pairs(pubDates) do if(pubDate['mainsnak']['datavalue']) then local timestamp = pubDate['mainsnak']['datavalue']['value']['time']; local accessdate = Date(timestamp); table.insert(ret,accessdate); end; end; end; if(#ret < 1) then return nil; end; table.sort(ret); return ret[1]; end; function p.printReviewScores(frame) local ret = ""; -- Loop all of "review scores" for this title local reviewscores = entity['claims']['P444']; if(reviewscores) then -- Find reviews that qualify for printing and insert into array. local reviewsToPrint = {} for i,review in pairs(reviewscores) do if(review['qualifiers'] ~= nil) then local scoreBy = nil if(review['qualifiers']['P447'] ~= nil and review['qualifiers']['P447'][1] ~= nil) then scoreBy = review['qualifiers']['P447'][1]['datavalue']['value']['numeric-id']; end; if(scoreBy == reviewer) then -- If template specified a system, we need to check for the specific system and only output that one. if(system == nil or system == "") then -- No system specified, so output each one found. table.insert(reviewsToPrint,review); else -- Get platform if it exists. if(review['qualifiers']['P400'] ~= nil and review['qualifiers']['P400'][1] ~= nil) then -- Try to match based on QID. local reviewSysId = review['qualifiers']['P400'][1]['datavalue']['value']['numeric-id']; if(systemId == reviewSysId) then table.insert(reviewsToPrint,review); else -- If that failed, try to match based on label. local systemName = mw.wikibase.label('Q'..reviewSysId); if(systemName ~= nil and string.upper(systemName) == system) then table.insert(reviewsToPrint,review); end; end; end; end; end; end; end; -- Sort the array by platform label. table.sort(reviewsToPrint, sortByPlatform); -- If a system was not specified, showSystem has defaulted to true. If this title only has one platform and one review, we will turn it off. -- Note: If the title has zero or more platforms defined, we leave showSystem on. We are unable to determine if this is a single-platform game. --if((system == nil or system == "") and #reviewsToPrint == 1 and entity['claims']['P400'] ~= nil and #entity['claims']['P400'] == 1) then -- Simplifying this based on discussion at [Template:Video game reviews]. If there's only one review, don't display system unless explicitly requested. if((system == nil or system == "") and #reviewsToPrint == 1) then showSystem = false; end; -- Print the reviews for i,review in ipairs(reviewsToPrint) do ret = ret .. printReviewRow(frame, review); end; end; if(ret ~= "") then ret = string.sub(ret, 1, -7); elseif(not showUpdateLink) then ret = nil; end; -- Add edit link at end if showUpdateLink is on. if(showUpdateLink) then ret = ret .. p.getUpdateLink(); end; return ret; end; return p 94a235941d390b6a0c1a6827ea88053a159e23e9 Template:MobyGames 10 473 947 946 2023-07-10T15:58:21Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:MobyGames]] wikitext text/x-wiki [https://www.mobygames.com/game{{Trim|{{{1|{{{id}}}}}}}} {{{2|{{{name|''{{PAGENAMEBASE}}''}}}}}}] at [[MobyGames]]<noinclude> {{documentation}} </noinclude> 2e12144d8e38e31c8db6a625b91644eae711192e Template:Moby game 10 474 949 948 2023-07-10T15:58:22Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Moby_game]] wikitext text/x-wiki #REDIRECT [[Template:MobyGames]] {{Redirect category shell| {{R from move}} }} 7729f8c0b996558d262215e400c5722da4270669 Template:BattleTech Universe 10 475 951 950 2023-07-10T15:58:22Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:BattleTech_Universe]] wikitext text/x-wiki {{Navbox |name = BattleTech Universe |title = ''[[BattleTech]]'' |listclass = hlist | state = {{{state|}}} |group1 = Universe |list1 = * [[BattleTech (fictional setting)]] |group2 = [[List of BattleTech games|Tabletop games]] |list2 = * ''[[Classic BattleTech]]'' * ''[[CityTech]]'' * ''[[AeroTech]]'' * ''[[BattleForce]]'' * ''[[BattleSpace]]'' * ''[[BattleTech Collectible Card Game]]'' * ''[[BattleTroops]]'' * ''[[MechWarrior (role-playing game)|MechWarrior]]'' * ''[[MechWarrior: Dark Age]]'' |group3 = [[MechWarrior|''MechWarrior'' series]] |list3 = * [[MechWarrior (1989 video game)|''MechWarrior'']] ** [[MechWarrior (1993 video game)|SNES]] ** ''[[MechWarrior 3050|3050]]'' * ''[[MechWarrior 2: 31st Century Combat|2: 31st Century Combat]]'' ** ''[[MechWarrior 2: Ghost Bear's Legacy|Ghost Bear's Legacy]]'' ** ''[[MechWarrior 2: Mercenaries|Mercenaries]]'' * ''[[MechWarrior 3|3]]'' ** ''[[MechWarrior 3#Pirate's Moon (expansion pack)|Pirate's Moon]]'' * ''[[MechWarrior 4: Vengeance|4: Vengeance]]'' ** ''[[MechWarrior 4: Vengeance#Black Knight|Black Knight]]'' ** ''[[MechWarrior 4: Mercenaries|Mercenaries]]'' * ''[[MechWarrior: Living Legends|Living Legends]]'' * ''[[MechWarrior Online|Online]]'' * ''[[MechWarrior: Tactical Command| Tactical Command]]'' * ''[[MechWarrior Tactics|Tactics]]'' * ''[[MechWarrior 5: Mercenaries|5: Mercenaries]]'' ** ''[[MechWarrior 5: Mercenaries#DLC1: Heroes of The Inner Sphere & Year One Update & Xbox release|Heroes of the Inner Sphere]]'' ** ''[[MechWarrior 5: Mercenaries#DLC2: Kestrel Lancers and PlayStation release|Kestrel Lancers]]'' |group4 = Other video games |list4 = * ''[[BattleTech: The Crescent Hawk's Inception]]'' * ''[[BattleTech: The Crescent Hawk's Revenge]]'' * ''[[MechAssault]]'' ** ''[[MechAssault 2: Lone Wolf|2: Lone Wolf]]'' ** ''[[MechAssault: Phantom War|Phantom War]]'' * ''[[MechCommander]]'' ** ''[[MechCommander 2|2]]'' * ''[[Multiplayer BattleTech 3025]]'' ** ''[[Multiplayer BattleTech: EGA|EGA]]'' ** ''[[Multiplayer BattleTech: Solaris|Solaris]]'' * ''[[BattleTech (video game)|BattleTech]]'' |group5 = Other |list5 = * [[FASA]] * [[WizKids]] * [[Fantasy Productions|FanPro]] * [[Catalyst Game Labs]] * [[BattleTech Centers]] * ''[[BattleTech: The Animated Series]]'' * ''[[BattleTechnology]]'' * [[List of BattleTech novels|List of novels]] * [[MechWarrior: Dark Age (novels)|''MechWarrior: Dark Age'']] (novels) | below ='''{{Icon|Category}} [[:Category:BattleTech|Category]]''' }}<noinclude>{{documentation|content= {{collapsible option}} [[Category:Video game navigational boxes by series]] }}</noinclude> cd82bd26e7554dafb8e21a7c589123c242625b39 Template:MechWarrior series 10 476 953 952 2023-07-10T15:58:22Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:MechWarrior_series]] wikitext text/x-wiki #REDIRECT [[Template:BattleTech Universe]] {{R from merge}} b1f8bc05076449b55471afcded8ec7fda35faabf Template:Wargaming Chicago-Baltimore 10 477 955 954 2023-07-10T15:58:23Z BigTa1k 2 1 revision imported from [[:wikipedia:Template:Wargaming_Chicago-Baltimore]] wikitext text/x-wiki {{Navbox | name = Wargaming Chicago-Baltimore | title = [[Wargaming Chicago-Baltimore]] | state = {{{state|autocollapse}}} | listclass = hlist | group1 = As Meyer/Glass Interactive | list1 = * ''[[Axis & Allies (1998 video game)|Axis & Allies]]'' * ''[[Avalon Hill's Diplomacy]]'' * ''[[Battleship: Surface Thunder]]'' | group2 = As Day 1 Studios | list2 = * ''[[MechAssault]]'' * ''[[MechAssault 2: Lone Wolf]]'' * ''[[Fracture (video game)|Fracture]]'' * ''[[F.E.A.R. 3]]'' | group3 = As Wargaming Chicago-Baltimore | list3 = * ''[[World of Tanks]]'' }} <noinclude> {{collapsible option}} {{Documentation}} [[Category:Video game navigational boxes by company]] </noinclude> 2698d524b1473072d3cde37ae1f9e7992ae6b32f Module:Message box/ambox.css 828 478 957 956 2023-07-10T15:58:23Z BigTa1k 2 1 revision imported from [[:wikipedia:Module:Message_box/ambox.css]] text text/plain /* {{pp|small=y}} */ .ambox { border: 1px solid #a2a9b1; /* @noflip */ border-left: 10px solid #36c; /* Default "notice" blue */ background-color: #fbfbfb; box-sizing: border-box; } /* Single border between stacked boxes. Take into account base templatestyles, * user styles, and Template:Dated maintenance category. * remove link selector when T200206 is fixed */ .ambox + link + .ambox, .ambox + link + style + .ambox, .ambox + link + link + .ambox, /* TODO: raise these as "is this really that necessary???". the change was Dec 2021 */ .ambox + .mw-empty-elt + link + .ambox, .ambox + .mw-empty-elt + link + style + .ambox, .ambox + .mw-empty-elt + link + link + .ambox { margin-top: -1px; } /* For the "small=left" option. */ /* must override .ambox + .ambox styles above */ html body.mediawiki .ambox.mbox-small-left { /* @noflip */ margin: 4px 1em 4px 0; overflow: hidden; width: 238px; border-collapse: collapse; font-size: 88%; line-height: 1.25em; } .ambox-speedy { /* @noflip */ border-left: 10px solid #b32424; /* Red */ background-color: #fee7e6; /* Pink */ } .ambox-delete { /* @noflip */ border-left: 10px solid #b32424; /* Red */ } .ambox-content { /* @noflip */ border-left: 10px solid #f28500; /* Orange */ } .ambox-style { /* @noflip */ border-left: 10px solid #fc3; /* Yellow */ } .ambox-move { /* @noflip */ border-left: 10px solid #9932cc; /* Purple */ } .ambox-protection { /* @noflip */ border-left: 10px solid #a2a9b1; /* Gray-gold */ } .ambox .mbox-text { border: none; /* @noflip */ padding: 0.25em 0.5em; width: 100%; } .ambox .mbox-image { border: none; /* @noflip */ padding: 2px 0 2px 0.5em; text-align: center; } .ambox .mbox-imageright { border: none; /* @noflip */ padding: 2px 0.5em 2px 0; text-align: center; } /* An empty narrow cell */ .ambox .mbox-empty-cell { border: none; padding: 0; width: 1px; } .ambox .mbox-image-div { width: 52px; } /* Hack around MobileFrontend being opinionated */ html.client-js body.skin-minerva .mbox-text-span { margin-left: 23px !important; } @media (min-width: 720px) { .ambox { margin: 0 10%; /* 10% = Will not overlap with other elements */ } } 29898fdc5160b39a8f580c76efe77afa1f6f58a4 MediaWiki:Common.css 8 480 960 2023-07-10T16:14:41Z BigTa1k 2 Created page with "/* * This is the CSS common to all desktop skins on en.Wikipedia. * Styling inside .mw-parser-output should generally use TemplateStyles. */ /* Reset italic styling set by user agent */ cite, dfn { font-style: inherit; } /* Straight quote marks for <q> */ q { quotes: '"' '"' "'" "'"; } /* Avoid collision of blockquote with floating elements by swapping margin and padding */ blockquote { overflow: hidden; margin: 1em 0; padding: 0 40px; } /* Consistent size for..." css text/css /* * This is the CSS common to all desktop skins on en.Wikipedia. * Styling inside .mw-parser-output should generally use TemplateStyles. */ /* Reset italic styling set by user agent */ cite, dfn { font-style: inherit; } /* Straight quote marks for <q> */ q { quotes: '"' '"' "'" "'"; } /* Avoid collision of blockquote with floating elements by swapping margin and padding */ blockquote { overflow: hidden; margin: 1em 0; padding: 0 40px; } /* Consistent size for <small>, <sub> and <sup> */ small { font-size: 85%; } .mw-body-content sub, .mw-body-content sup { font-size: 80%; } /* Same spacing for indented and unindented paragraphs on talk pages */ .ns-talk .mw-body-content dd { margin-top: 0.4em; margin-bottom: 0.4em; } /* Reduce page jumps by hiding collapsed/dismissed content */ .client-js .collapsible:not( .mw-made-collapsible).collapsed > tbody > tr:not(:first-child), /* Avoid FOUC/reflows on collapsed elements. */ /* This copies MediaWiki's solution for T42812 to apply to innercollapse/outercollapse (T325115). */ /* TODO: Use :is() selector at some reasonable future when support is good for Most Clients */ /* Reference: https://gerrit.wikimedia.org/g/mediawiki/core/+/ecda06cb2aef55b77c4b4d7ecda492d634419ead/resources/src/jquery/jquery.makeCollapsible.styles.less#75 */ .client-js .outercollapse .innercollapse.mw-collapsible:not( .mw-made-collapsible ) > p, .client-js .outercollapse .innercollapse.mw-collapsible:not( .mw-made-collapsible ) > table, .client-js .outercollapse .innercollapse.mw-collapsible:not( .mw-made-collapsible ) > thead + tbody, .client-js .outercollapse .innercollapse.mw-collapsible:not( .mw-made-collapsible ) tr:not( :first-child ), .client-js .outercollapse .innercollapse.mw-collapsible:not( .mw-made-collapsible ) .mw-collapsible-content, /* Hide charinsert base for those not using the gadget */ #editpage-specialchars { display: none; } /* Make the list of references smaller * Keep in sync with Template:Refbegin/styles.css * And Template:Reflist/styles.css */ ol.references { font-size: 90%; margin-bottom: 0.5em; } /* Cite customizations for Parsoid * Once everything uses the one true parser these are just customizations */ span[ rel="mw:referencedBy" ] { counter-reset: mw-ref-linkback 0; } span[ rel='mw:referencedBy' ] > a::before { content: counter( mw-ref-linkback, lower-alpha ); font-size: 80%; font-weight: bold; font-style: italic; } a[ rel="mw:referencedBy" ]::before { font-weight: bold; content: "^"; } span[ rel="mw:referencedBy" ]::before { content: "^ "; } .mw-ref > a[data-mw-group=lower-alpha]::after { content: '[' counter( mw-Ref, lower-alpha ) ']'; } .mw-ref > a[data-mw-group=upper-alpha]::after { content: '[' counter( mw-Ref, upper-alpha ) ']'; } .mw-ref > a[data-mw-group=decimal]::after { content: '[' counter( mw-Ref, decimal ) ']'; } .mw-ref > a[data-mw-group=lower-roman]::after { content: '[' counter( mw-Ref, lower-roman ) ']'; } .mw-ref > a[data-mw-group=upper-roman]::after { content: '[' counter( mw-Ref, upper-roman ) ']'; } .mw-ref > a[data-mw-group=lower-greek]::after { content: '[' counter( mw-Ref, lower-greek ) ']'; } /* Styling for jQuery makeCollapsible, matching that of collapseButton */ .mw-parser-output .mw-collapsible-toggle:not(.mw-ui-button) { font-weight: normal; padding-right: 0.2em; padding-left: 0.2em; } .mw-collapsible-leftside-toggle .mw-collapsible-toggle { /* @noflip */ float: left; } /* Lists in wikitable data cells are always left-aligned */ .wikitable td ul, .wikitable td ol, .wikitable td dl { /* @noflip */ text-align: left; } /* Change the external link icon to a PDF icon for all PDF files */ .mw-parser-output a[href$=".pdf"].external, .mw-parser-output a[href*=".pdf?"].external, .mw-parser-output a[href*=".pdf#"].external, .mw-parser-output a[href$=".PDF"].external, .mw-parser-output a[href*=".PDF?"].external, .mw-parser-output a[href*=".PDF#"].external { background: url("//upload.wikimedia.org/wikipedia/commons/4/4d/Icon_pdf_file.png") no-repeat right; /* @noflip */ padding: 8px 18px 8px 0; } /* System messages styled similarly to fmbox */ /* for .mw-warning-with-logexcerpt, behavior of this line differs between * the edit-protected notice and the special:Contribs for blocked users * The latter has specificity of 3 classes so we have to triple up here. */ .mw-warning-with-logexcerpt.mw-warning-with-logexcerpt.mw-warning-with-logexcerpt, div.mw-lag-warn-high, div.mw-cascadeprotectedwarning, div#mw-protect-cascadeon { clear: both; margin: 0.2em 0; border: 1px solid #bb7070; background-color: #ffdbdb; padding: 0.25em 0.9em; box-sizing: border-box; } /* default colors for partial block message */ /* gotta get over the hump introduced by the triple class above */ .mw-contributions-blocked-notice-partial .mw-warning-with-logexcerpt.mw-warning-with-logexcerpt { border-color: #fc3; background-color: #fef6e7; } /* Minimum thumb width */ figure[typeof~='mw:File/Thumb'], figure[typeof~='mw:File/Frame'], .thumbinner { min-width: 100px; } /* Prevent floating boxes from overlapping any category listings, file histories, edit previews, and edit [Show changes] views. */ #mw-subcategories, #mw-pages, #mw-category-media, #filehistory, #wikiPreview, #wikiDiff { clear: both; } /* Styling for tags in changes pages */ .mw-tag-markers { font-style: italic; font-size: 90%; } /* Hide stuff meant for accounts with special permissions. Made visible again in [[MediaWiki:Group-checkuser.css]], [[MediaWiki:Group-sysop.css]], [[MediaWiki:Group-abusefilter.css]], [[MediaWiki:Group-abusefilter-helper.css]], [[MediaWiki:Group-patroller.css]], [[MediaWiki:Group-templateeditor.css]], [[MediaWiki:Group-extendedmover.css]], [[MediaWiki:Group-extendedconfirmed.css]], and [[Mediawiki:Group-autoconfirmed.css]]. */ .checkuser-show, .sysop-show, .abusefilter-show, .abusefilter-helper-show, .patroller-show, .templateeditor-show, .extendedmover-show, .extendedconfirmed-show, .autoconfirmed-show, .user-show { display: none; } /* Hide the redlink generated by {{Editnotice}}, this overrides the ".sysop-show { display: none; }" above that applies to the same link as well. See [[phab:T45013]] Hide the images in editnotices to keep them readable in VE view. Long term, editnotices should become a core feature so that they can be designed responsive. */ .ve-ui-mwNoticesPopupTool-item .editnotice-redlink, .ve-ui-mwNoticesPopupTool-item .mbox-image, .ve-ui-mwNoticesPopupTool-item .mbox-imageright { display: none !important; } /* Remove bullets when there are multiple edit page warnings */ ul.permissions-errors { margin: 0; } ul.permissions-errors > li { list-style: none; } /* larger inline math */ span.mwe-math-mathml-inline { font-size: 118%; } /* Make <math display="block"> be left aligned with one space indent for * compatibility with style conventions */ .mwe-math-fallback-image-display, .mwe-math-mathml-display { margin-left: 1.6em !important; margin-top: 0.6em; margin-bottom: 0.6em; } .mwe-math-mathml-display math { display: inline; } @media screen { /* Put a chequered background behind images, only visible if they have transparency, * except on main, user, and portal namespaces */ body:not(.ns-0):not(.ns-2):not(.ns-100) .gallerybox .thumb img { background: #fff url(//upload.wikimedia.org/wikipedia/commons/5/5d/Checker-16x16.png) repeat; } /* Display "From Wikipedia, the free encyclopedia" in skins that support it, do not apply to print mode */ #siteSub { display: block; } } /* Hide FlaggedRevs notice UI when there are no pending changes */ .flaggedrevs_draft_synced, .flaggedrevs_stable_synced, /* "Temporary" to remove links in sidebar T255381 */ #t-upload, /* Hide broken download box on Special:Book pending T285400 */ .mw-special-Book #coll-downloadbox { display: none; } /* * BELOW HERE THERE BE SOONTOBE TEMPLATESTYLES THINGS; * SEE [[MediaWiki talk:Common.css/to do]] */ /* Infobox template style */ .infobox { border: 1px solid #a2a9b1; border-spacing: 3px; background-color: #f8f9fa; color: black; /* @noflip */ margin: 0.5em 0 0.5em 1em; padding: 0.2em; /* @noflip */ float: right; /* @noflip */ clear: right; font-size: 88%; line-height: 1.5em; width: 22em; } .infobox-header, .infobox-label, .infobox-above, .infobox-full-data, .infobox-data, .infobox-below, .infobox-subheader, .infobox-image, .infobox-navbar, /* Remove element selector when every .infobox thing is using the standard module/templates */ .infobox th, .infobox td { vertical-align: top; } .infobox-label, .infobox-data, /* Remove element selector when every .infobox thing is using the standard module/templates */ .infobox th, .infobox td { /* @noflip */ text-align: left; } /* Remove .infobox when element selectors above are removed */ .infobox .infobox-above, .infobox .infobox-title, /* Remove element selector when every .infobox thing is using the standard module/templates */ .infobox caption { font-size: 125%; font-weight: bold; text-align: center; } .infobox-title, /* Remove element selector when every .infobox thing is using the standard module/templates */ .infobox caption { padding: 0.2em; } /* Remove .infobox when element selectors above are removed */ .infobox .infobox-header, .infobox .infobox-subheader, .infobox .infobox-image, .infobox .infobox-full-data, .infobox .infobox-below { text-align: center; } /* Remove .infobox when element selectors above are removed */ .infobox .infobox-navbar { /* @noflip */ text-align: right; } /* Normal font styling for wikitable row headers with scope="row" tag */ .wikitable.plainrowheaders th[scope=row], .wikitable.plainrowheaders th[scope=rowgroup] { font-weight: normal; /* @noflip */ text-align: left; } /* Remove underlines from certain links */ .nounderlines a, .IPA a:link, .IPA a:visited { text-decoration: none !important; } /* Prevent line breaks in silly places where desired (nowrap) and links when we don't want them to (nowraplinks a) */ .nowrap, .nowraplinks a { white-space: nowrap; } /* But allow wrapping where desired: */ .wrap, .wraplinks a { white-space: normal; } /* texhtml class for inline math (based on generic times-serif class) */ span.texhtml { font-family: "Nimbus Roman No9 L", "Times New Roman", Times, serif; font-size: 118%; line-height: 1; white-space: nowrap; /* Force tabular and lining display for texhtml */ font-variant-numeric: lining-nums tabular-nums; font-kerning: none; } span.texhtml span.texhtml { font-size: 100%; } @media screen { .nochecker .gallerybox .thumb img { background-image: none; } } /* Put anything you mean to be a sitewide addition above the TemplateStyles * comment above. */ 6fb8f3b49a11a996e92a6014fa1eb15459a6daa4 MediaWiki:Common.js 8 481 961 2023-07-10T16:28:29Z BigTa1k 2 Created page with "/** * Keep code in MediaWiki:Common.js to a minimum as it is unconditionally * loaded for all users on every wiki page. If possible create a gadget that is * enabled by default instead of adding it here (since gadgets are fully * optimized ResourceLoader modules with possibility to add dependencies etc.) * * Since Common.js isn't a gadget, there is no place to declare its * dependencies, so we have to lazy load them with mw.loader.using on demand and * then execu..." javascript text/javascript /** * Keep code in MediaWiki:Common.js to a minimum as it is unconditionally * loaded for all users on every wiki page. If possible create a gadget that is * enabled by default instead of adding it here (since gadgets are fully * optimized ResourceLoader modules with possibility to add dependencies etc.) * * Since Common.js isn't a gadget, there is no place to declare its * dependencies, so we have to lazy load them with mw.loader.using on demand and * then execute the rest in the callback. In most cases these dependencies will * be loaded (or loading) already and the callback will not be delayed. In case a * dependency hasn't arrived yet it'll make sure those are loaded before this. */ /* global mw, $ */ /* jshint strict:false, browser:true */ mw.loader.using( [ 'mediawiki.util' ] ).done( function () { /* Begin of mw.loader.using callback */ /** * Map addPortletLink to mw.util * @deprecated: Use mw.util.addPortletLink instead. */ mw.log.deprecate( window, 'addPortletLink', mw.util.addPortletLink, 'Use mw.util.addPortletLink instead' ); /** * Test if an element has a certain class * @deprecated: Use $(element).hasClass() instead. */ mw.log.deprecate( window, 'hasClass', function ( element, className ) { return $( element ).hasClass( className ); }, 'Use jQuery.hasClass() instead' ); /** * @source www.mediawiki.org/wiki/Snippets/Load_JS_and_CSS_by_URL * @rev 6 */ var extraCSS = mw.util.getParamValue( 'withCSS' ), extraJS = mw.util.getParamValue( 'withJS' ); if ( extraCSS ) { if ( extraCSS.match( /^MediaWiki:[^&<>=%#]*\.css$/ ) ) { mw.loader.load( '/w/index.php?title=' + extraCSS + '&action=raw&ctype=text/css', 'text/css' ); } else { mw.notify( 'Only pages from the MediaWiki namespace are allowed.', { title: 'Invalid withCSS value' } ); } } if ( extraJS ) { if ( extraJS.match( /^MediaWiki:[^&<>=%#]*\.js$/ ) ) { mw.loader.load( '/w/index.php?title=' + extraJS + '&action=raw&ctype=text/javascript' ); } else { mw.notify( 'Only pages from the MediaWiki namespace are allowed.', { title: 'Invalid withJS value' } ); } } /** * WikiMiniAtlas * * Description: WikiMiniAtlas is a popup click and drag world map. * This script causes all of our coordinate links to display the WikiMiniAtlas popup button. * The script itself is located on the Meta-Wiki because it is used by many projects. * See [[Meta:WikiMiniAtlas]] for more information. * Note - use of this service is recommended to be replaced with mw:Help:Extension:Kartographer */ $( function () { var requireWikiminiatlas = $( 'a.external.text[href*="geohack"]' ).length || $( 'div.kmldata' ).length; if ( requireWikiminiatlas ) { mw.loader.load( '//meta.wikimedia.org/w/index.php?title=MediaWiki:Wikiminiatlas.js&action=raw&ctype=text/javascript' ); } } ); /** * Collapsible tables; reimplemented with mw-collapsible * Styling is also in place to avoid FOUC * * Allows tables to be collapsed, showing only the header. See [[Help:Collapsing]]. * @version 3.0.0 (2018-05-20) * @source https://www.mediawiki.org/wiki/MediaWiki:Gadget-collapsibleTables.js * @author [[User:R. Koot]] * @author [[User:Krinkle]] * @author [[User:TheDJ]] * @deprecated Since MediaWiki 1.20: Use class="mw-collapsible" instead which * is supported in MediaWiki core. Shimmable since MediaWiki 1.32 * * @param {jQuery} $content */ function makeCollapsibleMwCollapsible( $content ) { var $tables = $content .find( 'table.collapsible:not(.mw-collapsible)' ) .addClass( 'mw-collapsible' ); $.each( $tables, function ( index, table ) { // mw.log.warn( 'This page is using the deprecated class collapsible. Please replace it with mw-collapsible.'); if ( $( table ).hasClass( 'collapsed' ) ) { $( table ).addClass( 'mw-collapsed' ); // mw.log.warn( 'This page is using the deprecated class collapsed. Please replace it with mw-collapsed.'); } } ); if ( $tables.length > 0 ) { mw.loader.using( 'jquery.makeCollapsible' ).then( function () { $tables.makeCollapsible(); } ); } } mw.hook( 'wikipage.content' ).add( makeCollapsibleMwCollapsible ); /** * Add support to mw-collapsible for autocollapse, innercollapse and outercollapse * * Maintainers: TheDJ */ function mwCollapsibleSetup( $collapsibleContent ) { var $element, $toggle, autoCollapseThreshold = 2; $.each( $collapsibleContent, function ( index, element ) { $element = $( element ); if ( $element.hasClass( 'collapsible' ) ) { $element.find( 'tr:first > th:first' ).prepend( $element.find( 'tr:first > * > .mw-collapsible-toggle' ) ); } if ( $collapsibleContent.length >= autoCollapseThreshold && $element.hasClass( 'autocollapse' ) ) { $element.data( 'mw-collapsible' ).collapse(); } else if ( $element.hasClass( 'innercollapse' ) ) { if ( $element.parents( '.outercollapse' ).length > 0 ) { $element.data( 'mw-collapsible' ).collapse(); } } // because of colored backgrounds, style the link in the text color // to ensure accessible contrast $toggle = $element.find( '.mw-collapsible-toggle' ); if ( $toggle.length ) { // Make the toggle inherit text color (Updated for T333357 2023-04-29) if ( $toggle.parent()[ 0 ].style.color ) { $toggle.css( 'color', 'inherit' ); $toggle.find( '.mw-collapsible-text' ).css( 'color', 'inherit' ); } } } ); } mw.hook( 'wikipage.collapsibleContent' ).add( mwCollapsibleSetup ); /** * Magic editintros **************************************************** * * Description: Adds editintros on disambiguation pages and BLP pages. * Maintainers: [[User:RockMFR]] * * @param {string} name */ function addEditIntro( name ) { $( '.mw-editsection, #ca-edit, #ca-ve-edit' ).find( 'a' ).each( function ( i, el ) { el.href = $( this ).attr( 'href' ) + '&editintro=' + name; } ); } if ( mw.config.get( 'wgNamespaceNumber' ) === 0 ) { $( function () { if ( document.getElementById( 'disambigbox' ) ) { addEditIntro( 'Template:Disambig_editintro' ); } } ); $( function () { var cats = mw.config.get( 'wgCategories' ); if ( !cats ) { return; } if ( $.inArray( 'Living people', cats ) !== -1 || $.inArray( 'Possibly living people', cats ) !== -1 ) { addEditIntro( 'Template:BLP_editintro' ); } } ); } /* End of mw.loader.using callback */ } ); /* DO NOT ADD CODE BELOW THIS LINE */ d70a02c105be156bd0815623d6d289069c3e157e Template:Infobox videogame 10 482 963 962 2023-07-10T16:31:34Z BigTa1k 2 1 revision imported wikitext text/x-wiki #REDIRECT [[Template:Infobox video game]] b7846d88cb815822ebd1fd4960b74cb4513e5a6f Module:Template link general 828 483 965 964 2023-07-10T16:31:40Z BigTa1k 2 1 revision imported Scribunto text/plain -- This implements Template:Tlg local getArgs = require('Module:Arguments').getArgs local p = {} -- Is a string non-empty? local function _ne(s) return s ~= nil and s ~= "" end local nw = mw.text.nowiki local function addTemplate(s) local i, _ = s:find(':', 1, true) if i == nil then return 'Template:' .. s end local ns = s:sub(1, i - 1) if ns == '' or mw.site.namespaces[ns] then return s else return 'Template:' .. s end end local function trimTemplate(s) local needle = 'template:' if s:sub(1, needle:len()):lower() == needle then return s:sub(needle:len() + 1) else return s end end local function linkTitle(args) if _ne(args.nolink) then return args['1'] end local titleObj local titlePart = '[[' if args['1'] then -- This handles :Page and other NS titleObj = mw.title.new(args['1'], 'Template') else titleObj = mw.title.getCurrentTitle() end titlePart = titlePart .. (titleObj ~= nil and titleObj.fullText or addTemplate(args['1'])) local textPart = args.alttext if not _ne(textPart) then if titleObj ~= nil then textPart = titleObj:inNamespace("Template") and args['1'] or titleObj.fullText else -- redlink textPart = args['1'] end end if _ne(args.subst) then -- HACK: the ns thing above is probably broken textPart = 'subst:' .. textPart end if _ne(args.brace) then textPart = nw('{{') .. textPart .. nw('}}') elseif _ne(args.braceinside) then textPart = nw('{') .. textPart .. nw('}') end titlePart = titlePart .. '|' .. textPart .. ']]' if _ne(args.braceinside) then titlePart = nw('{') .. titlePart .. nw('}') end return titlePart end function p.main(frame) local args = getArgs(frame, { trim = true, removeBlanks = false }) return p._main(args) end function p._main(args) local bold = _ne(args.bold) or _ne(args.boldlink) or _ne(args.boldname) local italic = _ne(args.italic) or _ne(args.italics) local dontBrace = _ne(args.brace) or _ne(args.braceinside) local code = _ne(args.code) or _ne(args.tt) local show_result = _ne(args._show_result) local expand = _ne(args._expand) -- Build the link part local titlePart = linkTitle(args) if bold then titlePart = "'''" .. titlePart .. "'''" end if _ne(args.nowrapname) then titlePart = '<span class="nowrap">' .. titlePart .. '</span>' end -- Build the arguments local textPart = "" local textPartBuffer = "&#124;" local codeArguments = {} local codeArgumentsString = "" local i = 2 local j = 1 while args[i] do local val = args[i] if val ~= "" then if _ne(args.nowiki) then -- Unstrip nowiki tags first because calling nw on something that already contains nowiki tags will -- mangle the nowiki strip marker and result in literal UNIQ...QINU showing up val = nw(mw.text.unstripNoWiki(val)) end local k, v = string.match(val, "(.*)=(.*)") if not k then codeArguments[j] = val j = j + 1 else codeArguments[k] = v end codeArgumentsString = codeArgumentsString .. textPartBuffer .. val if italic then val = '<span style="font-style:italic;">' .. val .. '</span>' end textPart = textPart .. textPartBuffer .. val end i = i + 1 end -- final wrap local ret = titlePart .. textPart if not dontBrace then ret = nw('{{') .. ret .. nw('}}') end if _ne(args.a) then ret = nw('*') .. '&nbsp;' .. ret end if _ne(args.kbd) then ret = '<kbd>' .. ret .. '</kbd>' end if code then ret = '<code>' .. ret .. '</code>' elseif _ne(args.plaincode) then ret = '<code style="border:none;background:transparent;">' .. ret .. '</code>' end if _ne(args.nowrap) then ret = '<span class="nowrap">' .. ret .. '</span>' end --[[ Wrap as html?? local span = mw.html.create('span') span:wikitext(ret) --]] if _ne(args.debug) then ret = ret .. '\n<pre>' .. mw.text.encode(mw.dumpObject(args)) .. '</pre>' end if show_result then local result = mw.getCurrentFrame():expandTemplate{title = addTemplate(args[1]), args = codeArguments} ret = ret .. " → " .. result end if expand then local query = mw.text.encode('{{' .. addTemplate(args[1]) .. string.gsub(codeArgumentsString, textPartBuffer, "|") .. '}}') local url = mw.uri.fullUrl('special:ExpandTemplates', 'wpInput=' .. query) mw.log() ret = ret .. " [" .. tostring(url) .. "]" end return ret end return p c7307fa3959d308a2dd7fd2f5009c1ce6db3d122 Module:Distinguish 828 484 967 966 2023-07-10T16:31:42Z BigTa1k 2 1 revision imported Scribunto text/plain local mHatnote = require('Module:Hatnote') local mHatlist = require('Module:Hatnote list') local mArguments --initialize lazily local mTableTools --initialize lazily local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local p = {} function p.distinguish(frame) mArguments = require('Module:Arguments') mTableTools = require('Module:TableTools') local args = mArguments.getArgs(frame) local selfref = args.selfref local text = args.text args = mTableTools.compressSparseArray(args) return p._distinguish(args, text, selfref) end function p._distinguish(args, text, selfref) checkType("_distinguish", 1, args, 'table') if #args == 0 and not text then return '' end local text = string.format( 'Not to be confused with %s.', text or mHatlist.orList(args, true) ) hnOptions = {selfref = selfref} return mHatnote._hatnote(text, hnOptions) end return p 0364d14af01fc656ad1d898c5036fbd12a7ca938 Module:Format link 828 485 969 968 2023-07-10T16:31:42Z BigTa1k 2 1 revision imported Scribunto text/plain -------------------------------------------------------------------------------- -- Format link -- -- Makes a wikilink from the given link and display values. Links are escaped -- with colons if necessary, and links to sections are detected and displayed -- with " § " as a separator rather than the standard MediaWiki "#". Used in -- the {{format link}} template. -------------------------------------------------------------------------------- local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg local mArguments -- lazily initialise [[Module:Arguments]] local mError -- lazily initialise [[Module:Error]] local yesno -- lazily initialise [[Module:Yesno]] local p = {} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function getArgs(frame) -- Fetches the arguments from the parent frame. Whitespace is trimmed and -- blanks are removed. mArguments = require('Module:Arguments') return mArguments.getArgs(frame, {parentOnly = true}) end local function removeInitialColon(s) -- Removes the initial colon from a string, if present. return s:match('^:?(.*)') end local function maybeItalicize(s, shouldItalicize) -- Italicize s if s is a string and the shouldItalicize parameter is true. if s and shouldItalicize then return '<i>' .. s .. '</i>' else return s end end local function parseLink(link) -- Parse a link and return a table with the link's components. -- These components are: -- - link: the link, stripped of any initial colon (always present) -- - page: the page name (always present) -- - section: the page name (may be nil) -- - display: the display text, if manually entered after a pipe (may be nil) link = removeInitialColon(link) -- Find whether a faux display value has been added with the {{!}} magic -- word. local prePipe, display = link:match('^(.-)|(.*)$') link = prePipe or link -- Find the page, if it exists. -- For links like [[#Bar]], the page will be nil. local preHash, postHash = link:match('^(.-)#(.*)$') local page if not preHash then -- We have a link like [[Foo]]. page = link elseif preHash ~= '' then -- We have a link like [[Foo#Bar]]. page = preHash end -- Find the section, if it exists. local section if postHash and postHash ~= '' then section = postHash end return { link = link, page = page, section = section, display = display, } end local function formatDisplay(parsed, options) -- Formats a display string based on a parsed link table (matching the -- output of parseLink) and an options table (matching the input options for -- _formatLink). local page = maybeItalicize(parsed.page, options.italicizePage) local section = maybeItalicize(parsed.section, options.italicizeSection) if (not section) then return page elseif (not page) then return mw.ustring.format('§&nbsp;%s', section) else return mw.ustring.format('%s §&nbsp;%s', page, section) end end local function missingArgError(target) mError = require('Module:Error') return mError.error{message = 'Error: no link or target specified! ([[' .. target .. '#Errors|help]])' } end -------------------------------------------------------------------------------- -- Main functions -------------------------------------------------------------------------------- function p.formatLink(frame) -- The formatLink export function, for use in templates. yesno = require('Module:Yesno') local args = getArgs(frame) local link = args[1] or args.link local target = args[3] or args.target if not (link or target) then return missingArgError('Template:Format link') end return p._formatLink{ link = link, display = args[2] or args.display, target = target, italicizePage = yesno(args.italicizepage), italicizeSection = yesno(args.italicizesection), categorizeMissing = args.categorizemissing } end function p._formatLink(options) -- The formatLink export function, for use in modules. checkType('_formatLink', 1, options, 'table') local function check(key, expectedType) --for brevity checkTypeForNamedArg( '_formatLink', key, options[key], expectedType or 'string', true ) end check('link') check('display') check('target') check('italicizePage', 'boolean') check('italicizeSection', 'boolean') check('categorizeMissing') -- Normalize link and target and check that at least one is present if options.link == '' then options.link = nil end if options.target == '' then options.target = nil end if not (options.link or options.target) then return missingArgError('Module:Format link') end local parsed = parseLink(options.link) local display = options.display or parsed.display local catMissing = options.categorizeMissing local category = '' -- Find the display text if not display then display = formatDisplay(parsed, options) end -- Handle the target option if present if options.target then local parsedTarget = parseLink(options.target) parsed.link = parsedTarget.link parsed.page = parsedTarget.page end -- Test if page exists if a diagnostic category is specified if catMissing and (mw.ustring.len(catMissing) > 0) then local title = nil if parsed.page then title = mw.title.new(parsed.page) end if title and (not title.isExternal) then local success, exists = pcall(function() return title.exists end) if success and not exists then category = mw.ustring.format('[[Category:%s]]', catMissing) end end end -- Format the result as a link if parsed.link == display then return mw.ustring.format('[[:%s]]%s', parsed.link, category) else return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category) end end -------------------------------------------------------------------------------- -- Derived convenience functions -------------------------------------------------------------------------------- function p.formatPages(options, pages) -- Formats an array of pages using formatLink and the given options table, -- and returns it as an array. Nil values are not allowed. local ret = {} for i, page in ipairs(pages) do ret[i] = p._formatLink{ link = page, categorizeMissing = options.categorizeMissing, italicizePage = options.italicizePage, italicizeSection = options.italicizeSection } end return ret end return p 1253bdd2683ee4badc33856bfd5499b09a7dca1f Module:Hatnote 828 486 971 970 2023-07-10T16:31:43Z BigTa1k 2 1 revision imported Scribunto text/plain -------------------------------------------------------------------------------- -- Module:Hatnote -- -- -- -- This module produces hatnote links and links to related articles. It -- -- implements the {{hatnote}} and {{format link}} meta-templates and includes -- -- helper functions for other Lua hatnote modules. -- -------------------------------------------------------------------------------- local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg local mArguments -- lazily initialise [[Module:Arguments]] local yesno -- lazily initialise [[Module:Yesno]] local formatLink -- lazily initialise [[Module:Format link]] ._formatLink local p = {} -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local function getArgs(frame) -- Fetches the arguments from the parent frame. Whitespace is trimmed and -- blanks are removed. mArguments = require('Module:Arguments') return mArguments.getArgs(frame, {parentOnly = true}) end local function removeInitialColon(s) -- Removes the initial colon from a string, if present. return s:match('^:?(.*)') end function p.defaultClasses(inline) -- Provides the default hatnote classes as a space-separated string; useful -- for hatnote-manipulation modules like [[Module:Hatnote group]]. return (inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' .. 'navigation-not-searchable' end function p.disambiguate(page, disambiguator) -- Formats a page title with a disambiguation parenthetical, -- i.e. "Example" → "Example (disambiguation)". checkType('disambiguate', 1, page, 'string') checkType('disambiguate', 2, disambiguator, 'string', true) disambiguator = disambiguator or 'disambiguation' return mw.ustring.format('%s (%s)', page, disambiguator) end function p.findNamespaceId(link, removeColon) -- Finds the namespace id (namespace number) of a link or a pagename. This -- function will not work if the link is enclosed in double brackets. Colons -- are trimmed from the start of the link by default. To skip colon -- trimming, set the removeColon parameter to false. checkType('findNamespaceId', 1, link, 'string') checkType('findNamespaceId', 2, removeColon, 'boolean', true) if removeColon ~= false then link = removeInitialColon(link) end local namespace = link:match('^(.-):') if namespace then local nsTable = mw.site.namespaces[namespace] if nsTable then return nsTable.id end end return 0 end function p.makeWikitextError(msg, helpLink, addTrackingCategory, title) -- Formats an error message to be returned to wikitext. If -- addTrackingCategory is not false after being returned from -- [[Module:Yesno]], and if we are not on a talk page, a tracking category -- is added. checkType('makeWikitextError', 1, msg, 'string') checkType('makeWikitextError', 2, helpLink, 'string', true) yesno = require('Module:Yesno') title = title or mw.title.getCurrentTitle() -- Make the help link text. local helpText if helpLink then helpText = ' ([[' .. helpLink .. '|help]])' else helpText = '' end -- Make the category text. local category if not title.isTalkPage -- Don't categorise talk pages and title.namespace ~= 2 -- Don't categorise userspace and yesno(addTrackingCategory) ~= false -- Allow opting out then category = 'Hatnote templates with errors' category = mw.ustring.format( '[[%s:%s]]', mw.site.namespaces[14].name, category ) else category = '' end return mw.ustring.format( '<strong class="error">Error: %s%s.</strong>%s', msg, helpText, category ) end local curNs = mw.title.getCurrentTitle().namespace p.missingTargetCat = --Default missing target category, exported for use in related modules ((curNs == 0) or (curNs == 14)) and 'Articles with hatnote templates targeting a nonexistent page' or nil function p.quote(title) --Wraps titles in quotation marks. If the title starts/ends with a quotation --mark, kerns that side as with {{-'}} local quotationMarks = { ["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true } local quoteLeft, quoteRight = -- Test if start/end are quotation marks quotationMarks[string.sub(title, 1, 1)], quotationMarks[string.sub(title, -1, -1)] if quoteLeft or quoteRight then title = mw.html.create("span"):wikitext(title) end if quoteLeft then title:css("padding-left", "0.15em") end if quoteRight then title:css("padding-right", "0.15em") end return '"' .. tostring(title) .. '"' end -------------------------------------------------------------------------------- -- Hatnote -- -- Produces standard hatnote text. Implements the {{hatnote}} template. -------------------------------------------------------------------------------- function p.hatnote(frame) local args = getArgs(frame) local s = args[1] if not s then return p.makeWikitextError( 'no text specified', 'Template:Hatnote#Errors', args.category ) end return p._hatnote(s, { extraclasses = args.extraclasses, selfref = args.selfref }) end function p._hatnote(s, options) checkType('_hatnote', 1, s, 'string') checkType('_hatnote', 2, options, 'table', true) options = options or {} local inline = options.inline local hatnote = mw.html.create(inline == 1 and 'span' or 'div') local extraclasses if type(options.extraclasses) == 'string' then extraclasses = options.extraclasses end hatnote :attr('role', 'note') :addClass(p.defaultClasses(inline)) :addClass(extraclasses) :addClass(options.selfref and 'selfref' or nil) :wikitext(s) return mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' } } .. tostring(hatnote) end return p 3ae1ed7094c5005ca0896395ec9a587287a0bef1 Module:Hatnote/styles.css 828 487 973 972 2023-07-10T16:31:43Z BigTa1k 2 1 revision imported text text/plain /* {{pp|small=y}} */ .hatnote { font-style: italic; } /* Limit structure CSS to divs because of [[Module:Hatnote inline]] */ div.hatnote { /* @noflip */ padding-left: 1.6em; margin-bottom: 0.5em; } .hatnote i { font-style: normal; } /* The templatestyles element inserts a link element before hatnotes. * TODO: Remove link if/when WMF resolves T200206 */ .hatnote + link + .hatnote { margin-top: -0.5em; } 44680ffd6e888866df2cdfa0341af9c7b97da94c Module:Hatnote list 828 488 975 974 2023-07-10T16:31:43Z BigTa1k 2 1 revision imported Scribunto text/plain -------------------------------------------------------------------------------- -- Module:Hatnote list -- -- -- -- This module produces and formats lists for use in hatnotes. In particular, -- -- it implements the for-see list, i.e. lists of "For X, see Y" statements, -- -- as used in {{about}}, {{redirect}}, and their variants. Also introduced -- -- are andList & orList helpers for formatting lists with those conjunctions. -- -------------------------------------------------------------------------------- local mArguments --initialize lazily local mFormatLink = require('Module:Format link') local mHatnote = require('Module:Hatnote') local libraryUtil = require('libraryUtil') local checkType = libraryUtil.checkType local p = {} -------------------------------------------------------------------------------- -- List stringification helper functions -- -- These functions are used for stringifying lists, usually page lists inside -- the "Y" portion of "For X, see Y" for-see items. -------------------------------------------------------------------------------- --default options table used across the list stringification functions local stringifyListDefaultOptions = { conjunction = "and", separator = ",", altSeparator = ";", space = " ", formatted = false } --Searches display text only local function searchDisp(haystack, needle) return string.find( string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle ) end -- Stringifies a list generically; probably shouldn't be used directly local function stringifyList(list, options) -- Type-checks, defaults, and a shortcut checkType("stringifyList", 1, list, "table") if #list == 0 then return nil end checkType("stringifyList", 2, options, "table", true) options = options or {} for k, v in pairs(stringifyListDefaultOptions) do if options[k] == nil then options[k] = v end end local s = options.space -- Format the list if requested if options.formatted then list = mFormatLink.formatPages( {categorizeMissing = mHatnote.missingTargetCat}, list ) end -- Set the separator; if any item contains it, use the alternate separator local separator = options.separator for k, v in pairs(list) do if searchDisp(v, separator) then separator = options.altSeparator break end end -- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§" local conjunction = s .. options.conjunction .. s if #list == 2 and searchDisp(list[1], "§") or #list > 2 then conjunction = separator .. conjunction end -- Return the formatted string return mw.text.listToText(list, separator .. s, conjunction) end --DRY function function p.conjList (conj, list, fmt) return stringifyList(list, {conjunction = conj, formatted = fmt}) end -- Stringifies lists with "and" or "or" function p.andList (...) return p.conjList("and", ...) end function p.orList (...) return p.conjList("or", ...) end -------------------------------------------------------------------------------- -- For see -- -- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the -- {{about}} and {{redirect}} templates and their variants. -------------------------------------------------------------------------------- --default options table used across the forSee family of functions local forSeeDefaultOptions = { andKeyword = 'and', title = mw.title.getCurrentTitle().text, otherText = 'other uses', forSeeForm = 'For %s, see %s.', } --Collapses duplicate punctuation local function punctuationCollapse (text) local replacements = { ["%.%.$"] = ".", ["%?%.$"] = "?", ["%!%.$"] = "!", ["%.%]%]%.$"] = ".]]", ["%?%]%]%.$"] = "?]]", ["%!%]%]%.$"] = "!]]" } for k, v in pairs(replacements) do text = string.gsub(text, k, v) end return text end -- Structures arguments into a table for stringification, & options function p.forSeeArgsToTable (args, from, options) -- Type-checks and defaults checkType("forSeeArgsToTable", 1, args, 'table') checkType("forSeeArgsToTable", 2, from, 'number', true) from = from or 1 checkType("forSeeArgsToTable", 3, options, 'table', true) options = options or {} for k, v in pairs(forSeeDefaultOptions) do if options[k] == nil then options[k] = v end end -- maxArg's gotten manually because getArgs() and table.maxn aren't friends local maxArg = 0 for k, v in pairs(args) do if type(k) == 'number' and k > maxArg then maxArg = k end end -- Structure the data out from the parameter list: -- * forTable is the wrapper table, with forRow rows -- * Rows are tables of a "use" string & a "pages" table of pagename strings -- * Blanks are left empty for defaulting elsewhere, but can terminate list local forTable = {} local i = from local terminated = false -- If there is extra text, and no arguments are given, give nil value -- to not produce default of "For other uses, see foo (disambiguation)" if options.extratext and i > maxArg then return nil end -- Loop to generate rows repeat -- New empty row local forRow = {} -- On blank use, assume list's ended & break at end of this loop forRow.use = args[i] if not args[i] then terminated = true end -- New empty list of pages forRow.pages = {} -- Insert first pages item if present table.insert(forRow.pages, args[i + 1]) -- If the param after next is "and", do inner loop to collect params -- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3} while args[i + 2] == options.andKeyword do if args[i + 3] then table.insert(forRow.pages, args[i + 3]) end -- Increment to next "and" i = i + 2 end -- Increment to next use i = i + 2 -- Append the row table.insert(forTable, forRow) until terminated or i > maxArg return forTable end -- Stringifies a table as formatted by forSeeArgsToTable function p.forSeeTableToString (forSeeTable, options) -- Type-checks and defaults checkType("forSeeTableToString", 1, forSeeTable, "table", true) checkType("forSeeTableToString", 2, options, "table", true) options = options or {} for k, v in pairs(forSeeDefaultOptions) do if options[k] == nil then options[k] = v end end -- Stringify each for-see item into a list local strList = {} if forSeeTable then for k, v in pairs(forSeeTable) do local useStr = v.use or options.otherText local pagesStr = p.andList(v.pages, true) or mFormatLink._formatLink{ categorizeMissing = mHatnote.missingTargetCat, link = mHatnote.disambiguate(options.title) } local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr) forSeeStr = punctuationCollapse(forSeeStr) table.insert(strList, forSeeStr) end end if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end -- Return the concatenated list return table.concat(strList, ' ') end -- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps -- but not blank/whitespace values. Ignores named args and args < "from". function p._forSee (args, from, options) local forSeeTable = p.forSeeArgsToTable(args, from, options) return p.forSeeTableToString(forSeeTable, options) end -- As _forSee, but uses the frame. function p.forSee (frame, from, options) mArguments = require('Module:Arguments') return p._forSee(mArguments.getArgs(frame), from, options) end return p d0828422b1aa0d0d0092d699d059c9e882260398 Module:Sidebar 828 489 977 976 2023-07-10T16:31:45Z BigTa1k 2 1 revision imported Scribunto text/plain require('strict') local cfg = mw.loadData('Module:Sidebar/configuration') local p = {} local getArgs = require('Module:Arguments').getArgs --[[ Categorizes calling templates and modules with a 'style' parameter of any sort for tracking to convert to TemplateStyles. TODO after a long cleanup: Catch sidebars in other namespaces than Template and Module. TODO would probably want to remove /log and /archive as CS1 does ]] local function categorizeTemplatesWithInlineStyles(args) local title = mw.title.getCurrentTitle() if title.namespace ~= 10 and title.namespace ~= 828 then return '' end for _, pattern in ipairs (cfg.i18n.pattern.uncategorized_conversion_titles) do if title.text:match(pattern) then return '' end end for key, _ in pairs(args) do if mw.ustring.find(key, cfg.i18n.pattern.style_conversion) or key == 'width' then return cfg.i18n.category.conversion end end end --[[ For compatibility with the original {{sidebar with collapsible lists}} implementation, which passed some parameters through {{#if}} to trim their whitespace. This also triggered the automatic newline behavior. ]] -- See ([[meta:Help:Newlines and spaces#Automatic newline]]) local function trimAndAddAutomaticNewline(s) s = mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1") if mw.ustring.find(s, '^[#*:;]') or mw.ustring.find(s, '^{|') then return '\n' .. s else return s end end --[[ Finds whether a sidebar has a subgroup sidebar. ]] local function hasSubgroup(s) if mw.ustring.find(s, cfg.i18n.pattern.subgroup) then return true else return false end end local function has_navbar(navbar_mode, sidebar_name) return navbar_mode ~= cfg.i18n.navbar_none and navbar_mode ~= cfg.i18n.navbar_off and ( sidebar_name or mw.getCurrentFrame():getParent():getTitle():gsub(cfg.i18n.pattern.sandbox, '') ~= cfg.i18n.title_not_to_add_navbar ) end local function has_list_class(args, htmlclass) local patterns = { '^' .. htmlclass .. '$', '%s' .. htmlclass .. '$', '^' .. htmlclass .. '%s', '%s' .. htmlclass .. '%s' } for arg, value in pairs(args) do if type(arg) == 'string' and mw.ustring.find(arg, 'class') then for _, pattern in ipairs(patterns) do if mw.ustring.find(args[arg] or '', pattern) then return true end end end end return false end -- there are a lot of list classes in the wild, so we add their TemplateStyles local function add_list_styles(args) local frame = mw.getCurrentFrame() local function add_list_templatestyles(htmlclass, templatestyles) if has_list_class(args, htmlclass) then return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } else return '' end end local plainlist_styles = add_list_templatestyles('plainlist', cfg.i18n.plainlist_templatestyles) local hlist_styles = add_list_templatestyles('hlist', cfg.i18n.hlist_templatestyles) -- a second workaround for [[phab:T303378]] -- when that issue is fixed, we can actually use has_navbar not to emit the -- tag here if we want if has_navbar(args.navbar, args.name) and hlist_styles == '' then hlist_styles = frame:extensionTag{ name = 'templatestyles', args = { src = cfg.i18n.hlist_templatestyles} } end -- hlist -> plainlist is best-effort to preserve old Common.css ordering. [hlist_note] return hlist_styles .. plainlist_styles end -- work around [[phab:T303378]] -- for each arg: find all the templatestyles strip markers, insert them into a -- table. then remove all templatestyles markers from the arg local function move_hiding_templatestyles(args) local gfind = string.gfind local gsub = string.gsub local templatestyles_markers = {} local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)' for k, arg in pairs(args) do for marker in gfind(arg, strip_marker_pattern) do table.insert(templatestyles_markers, marker) end args[k] = gsub(arg, strip_marker_pattern, '') end return templatestyles_markers end --[[ Main sidebar function. Takes the frame, args, and an optional collapsibleClass. The collapsibleClass is and should be used only for sidebars with collapsible lists, as in p.collapsible. ]] function p.sidebar(frame, args, collapsibleClass) if not args then args = getArgs(frame) end local hiding_templatestyles = table.concat(move_hiding_templatestyles(args)) local root = mw.html.create() local child = args.child and mw.text.trim(args.child) == cfg.i18n.child_yes root = root:tag('table') if not child then root :addClass(cfg.i18n.class.sidebar) -- force collapsibleclass to be sidebar-collapse otherwise output nothing :addClass(collapsibleClass == cfg.i18n.class.collapse and cfg.i18n.class.collapse or nil) :addClass('nomobile') :addClass(args.float == cfg.i18n.float_none and cfg.i18n.class.float_none or nil) :addClass(args.float == cfg.i18n.float_left and cfg.i18n.class.float_left or nil) :addClass(args.wraplinks ~= cfg.i18n.wrap_true and cfg.i18n.class.wraplinks or nil) :addClass(args.bodyclass or args.class) :css('width', args.width or nil) :cssText(args.bodystyle or args.style) if args.outertitle then root :tag('caption') :addClass(cfg.i18n.class.outer_title) :addClass(args.outertitleclass) :cssText(args.outertitlestyle) :wikitext(args.outertitle) end if args.topimage then local imageCell = root:tag('tr'):tag('td') imageCell :addClass(cfg.i18n.class.top_image) :addClass(args.topimageclass) :cssText(args.topimagestyle) :wikitext(args.topimage) if args.topcaption then imageCell :tag('div') :addClass(cfg.i18n.class.top_caption) :cssText(args.topcaptionstyle) :wikitext(args.topcaption) end end if args.pretitle then root :tag('tr') :tag('td') :addClass(args.topimage and cfg.i18n.class.pretitle_with_top_image or cfg.i18n.class.pretitle) :addClass(args.pretitleclass) :cssText(args.basestyle) :cssText(args.pretitlestyle) :wikitext(args.pretitle) end else root :addClass(cfg.i18n.class.subgroup) :addClass(args.bodyclass or args.class) :cssText(args.bodystyle or args.style) end if args.title then if child then root :wikitext(args.title) else root :tag('tr') :tag('th') :addClass(args.pretitle and cfg.i18n.class.title_with_pretitle or cfg.i18n.class.title) :addClass(args.titleclass) :cssText(args.basestyle) :cssText(args.titlestyle) :wikitext(args.title) end end if args.image then local imageCell = root:tag('tr'):tag('td') imageCell :addClass(cfg.i18n.class.image) :addClass(args.imageclass) :cssText(args.imagestyle) :wikitext(args.image) if args.caption then imageCell :tag('div') :addClass(cfg.i18n.class.caption) :cssText(args.captionstyle) :wikitext(args.caption) end end if args.above then root :tag('tr') :tag('td') :addClass(cfg.i18n.class.above) :addClass(args.aboveclass) :cssText(args.abovestyle) :newline() -- newline required for bullet-points to work :wikitext(args.above) end local rowNums = {} for k, v in pairs(args) do k = '' .. k local num = k:match('^heading(%d+)$') or k:match('^content(%d+)$') if num then table.insert(rowNums, tonumber(num)) end end table.sort(rowNums) -- remove duplicates from the list (e.g. 3 will be duplicated if both heading3 -- and content3 are specified) for i = #rowNums, 1, -1 do if rowNums[i] == rowNums[i - 1] then table.remove(rowNums, i) end end for i, num in ipairs(rowNums) do local heading = args['heading' .. num] if heading then root :tag('tr') :tag('th') :addClass(cfg.i18n.class.heading) :addClass(args.headingclass) :addClass(args['heading' .. num .. 'class']) :cssText(args.basestyle) :cssText(args.headingstyle) :cssText(args['heading' .. num .. 'style']) :newline() :wikitext(heading) end local content = args['content' .. num] if content then root :tag('tr') :tag('td') :addClass(hasSubgroup(content) and cfg.i18n.class.content_with_subgroup or cfg.i18n.class.content) :addClass(args.contentclass) :addClass(args['content' .. num .. 'class']) :cssText(args.contentstyle) :cssText(args['content' .. num .. 'style']) :newline() :wikitext(content) :done() -- Without a linebreak after the </td>, a nested list like -- "* {{hlist| ...}}" doesn't parse correctly. :newline() end end if args.below then root :tag('tr') :tag('td') :addClass(cfg.i18n.class.below) :addClass(args.belowclass) :cssText(args.belowstyle) :newline() :wikitext(args.below) end if not child and has_navbar(args.navbar, args.name) then root :tag('tr') :tag('td') :addClass(cfg.i18n.class.navbar) :cssText(args.navbarstyle) :wikitext(require('Module:Navbar')._navbar{ args.name, mini = 1, fontstyle = args.navbarfontstyle }) end local base_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = cfg.i18n.templatestyles } } local templatestyles = '' if args['templatestyles'] and args['templatestyles'] ~= '' then templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['templatestyles'] } } end local child_templatestyles = '' if args['child templatestyles'] and args['child templatestyles'] ~= '' then child_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['child templatestyles'] } } end local grandchild_templatestyles = '' if args['grandchild templatestyles'] and args['grandchild templatestyles'] ~= '' then grandchild_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['grandchild templatestyles'] } } end return table.concat({ add_list_styles(args), -- see [hlist_note] above about ordering base_templatestyles, templatestyles, child_templatestyles, grandchild_templatestyles, hiding_templatestyles, tostring(root), (child and cfg.i18n.category.child or ''), categorizeTemplatesWithInlineStyles(args) }) end local function list_title(args, is_centered_list_titles, num) local title_text = trimAndAddAutomaticNewline(args['list' .. num .. 'title'] or cfg.i18n.default_list_title) local title if is_centered_list_titles then -- collapsible can be finicky, so provide some CSS/HTML to support title = mw.html.create('div') :addClass(cfg.i18n.class.list_title_centered) :wikitext(title_text) else title = mw.html.create() :wikitext(title_text) end local title_container = mw.html.create('div') :addClass(cfg.i18n.class.list_title) -- don't /need/ a listnumtitleclass because you can do -- .templateclass .listnumclass .sidebar-list-title :addClass(args.listtitleclass) :cssText(args.basestyle) :cssText(args.listtitlestyle) :cssText(args['list' .. num .. 'titlestyle']) :node(title) :done() return title_container end --[[ Main entry point for sidebar with collapsible lists. Does the work of creating the collapsible lists themselves and including them into the args. ]] function p.collapsible(frame) local args = getArgs(frame) if not args.name and frame:getParent():getTitle():gsub(cfg.i18n.pattern.collapse_sandbox, '') == cfg.i18n.collapse_title_not_to_add_navbar then args.navbar = cfg.i18n.navbar_none end local contentArgs = {} local is_centered_list_titles = false if args['centered list titles'] and args['centered list titles'] ~= '' then is_centered_list_titles = true end for k, v in pairs(args) do local num = string.match(k, '^list(%d+)$') if num then local expand = args.expanded and (args.expanded == 'all' or args.expanded == args['list' .. num .. 'name']) local row = mw.html.create('div') row :addClass(cfg.i18n.class.list) :addClass('mw-collapsible') :addClass((not expand) and 'mw-collapsed' or nil) :addClass(args['list' .. num .. 'class']) :cssText(args.listframestyle) :cssText(args['list' .. num .. 'framestyle']) :node(list_title(args, is_centered_list_titles, num)) :tag('div') :addClass(cfg.i18n.class.list_content) :addClass('mw-collapsible-content') -- don't /need/ a listnumstyleclass because you can do -- .templatename .listnumclass .sidebar-list :addClass(args.listclass) :cssText(args.liststyle) :cssText(args['list' .. num .. 'style']) :wikitext(trimAndAddAutomaticNewline(args['list' .. num])) contentArgs['content' .. num] = tostring(row) end end for k, v in pairs(contentArgs) do args[k] = v end return p.sidebar(frame, args, cfg.i18n.class.collapse) end return p 71fe765846593e025ca2f94371315e9dbb5bb4d2 Module:Sidebar/configuration 828 490 979 978 2023-07-10T16:31:45Z BigTa1k 2 1 revision imported Scribunto text/plain return { i18n = { child_yes = 'yes', float_none = 'none', float_left = 'left', wrap_true = 'true', navbar_none = 'none', navbar_off = 'off', default_list_title = 'List', title_not_to_add_navbar = 'Template:Sidebar', collapse_title_not_to_add_navbar = 'Template:Sidebar with collapsible lists', templatestyles = 'Module:Sidebar/styles.css', hlist_templatestyles = 'Hlist/styles.css', plainlist_templatestyles = 'Plainlist/styles.css', category = { child = '[[Category:Pages using sidebar with the child parameter]]', conversion = '[[Category:Sidebars with styles needing conversion]]' }, pattern = { collapse_sandbox = '/sandbox$', sandbox = '/sandbox$', subgroup = 'sidebar%-subgroup', style_conversion = 'style$', uncategorized_conversion_titles = { '/[Ss]andbox', '/[Tt]estcases', '/[Dd]oc$' } }, class = { sidebar = 'sidebar', subgroup = 'sidebar-subgroup', collapse = 'sidebar-collapse', float_none = 'sidebar-none', float_left = 'sidebar-left', wraplinks = 'nowraplinks', outer_title = 'sidebar-outer-title', top_image = 'sidebar-top-image', top_caption = 'sidebar-top-caption', pretitle = 'sidebar-pretitle', pretitle_with_top_image = 'sidebar-pretitle-with-top-image', title = 'sidebar-title', title_with_pretitle = 'sidebar-title-with-pretitle', image = 'sidebar-image', caption = 'sidebar-caption', above = 'sidebar-above', heading = 'sidebar-heading', content = 'sidebar-content', content_with_subgroup = 'sidebar-content-with-subgroup', below = 'sidebar-below', navbar = 'sidebar-navbar', list = 'sidebar-list', list_title = 'sidebar-list-title', list_title_centered = 'sidebar-list-title-c', list_content = 'sidebar-list-content' } } } dc2a980ac2162a898f7c21e6d6ba7e994dfeb315 Module:Sidebar/styles.css 828 491 981 980 2023-07-10T16:31:46Z BigTa1k 2 1 revision imported text text/plain /* {{pp-template}} */ /* TODO: Invert width design to be "mobile first" */ .sidebar { /* TODO: Ask if we should have max-width 22em instead */ width: 22em; /* @noflip */ float: right; /* @noflip */ clear: right; /* @noflip */ margin: 0.5em 0 1em 1em; background: #f8f9fa; border: 1px solid #aaa; padding: 0.2em; text-align: center; line-height: 1.4em; font-size: 88%; border-collapse: collapse; /* Timeless has display: none on .nomobile at mobile resolutions, so we * unhide it with display: table and let precedence and proximity win. */ display: table; } /* Unfortunately, so does Minerva desktop, except Minerva drops an * !important on the declaration. So we have to be mean for Minerva users. * Mobile removes the element entirely with `wgMFRemovableClasses` in * https://github.com/wikimedia/operations-mediawiki-config/blob/master/ wmf-config/InitialiseSettings.php#L16992 * which is why displaying it categorically with display: table works. * We don't really want to expose the generic user in the wild on mobile to have * to deal with sidebars. (Maybe the ones with collapsible lists, so that * might be an improvement. That is blocked on [[:phab:T111565]].) */ body.skin-minerva .sidebar { display: table !important; /* also, minerva is way too aggressive about other stylings on tables. * TODO remove when this template gets moved to a div. plans on talk page. * We always float right on Minerva because that's a lot of extra CSS * otherwise. */ float: right !important; margin: 0.5em 0 1em 1em !important; } .sidebar-subgroup { width: 100%; margin: 0; border-spacing: 0; } .sidebar-left { /* @noflip */ float: left; /* @noflip */ clear: left; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-none { float: none; clear: both; /* @noflip */ margin: 0.5em 1em 1em 0; } .sidebar-outer-title { padding: 0 0.4em 0.2em; font-size: 125%; line-height: 1.2em; font-weight: bold; } .sidebar-top-image { padding: 0.4em; } .sidebar-top-caption, .sidebar-pretitle-with-top-image, .sidebar-caption { padding: 0.2em 0.4em 0; line-height: 1.2em; } .sidebar-pretitle { padding: 0.4em 0.4em 0; line-height: 1.2em; } .sidebar-title, .sidebar-title-with-pretitle { padding: 0.2em 0.8em; font-size: 145%; line-height: 1.2em; } .sidebar-title-with-pretitle { padding: 0.1em 0.4em; } .sidebar-image { padding: 0.2em 0.4em 0.4em; } .sidebar-heading { padding: 0.1em 0.4em; } .sidebar-content { padding: 0 0.5em 0.4em; } .sidebar-content-with-subgroup { padding: 0.1em 0.4em 0.2em; } .sidebar-above, .sidebar-below { padding: 0.3em 0.8em; font-weight: bold; } .sidebar-collapse .sidebar-above, .sidebar-collapse .sidebar-below { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; } .sidebar-navbar { text-align: right; font-size: 115%; padding: 0 0.4em 0.4em; } .sidebar-list-title { padding: 0 0.4em; text-align: left; font-weight: bold; line-height: 1.6em; font-size: 105%; } /* centered text with mw-collapsible headers is finicky */ .sidebar-list-title-c { padding: 0 0.4em; text-align: center; margin: 0 3.3em; } @media (max-width: 720px) { /* users have wide latitude to set arbitrary width and margin :( "Super-specific" selector to prevent overriding this appearance by lower level sidebars too */ body.mediawiki .sidebar { width: 100% !important; clear: both; float: none !important; /* Remove when we div based; Minerva is dumb */ margin-left: 0 !important; margin-right: 0 !important; } /* TODO: We might consider making all links wrap at small resolutions and then * only introduce nowrap at higher resolutions. Do when we invert the media * query. */ } 7d621b35a37807a103b59075851fe36201204ceb Module:Parameter names example 828 492 983 982 2023-07-10T16:31:49Z BigTa1k 2 1 revision imported Scribunto text/plain -- This module implements {{parameter names example}}. local p = {} local function makeParam(s) local lb = '&#123;' local rb = '&#125;' return lb:rep(3) .. s .. rb:rep(3) end local function italicize(s) return "''" .. s .. "''" end local function plain(s) return s end function p._main(args, frame) -- Find how we want to format the arguments to the template. local formatFunc if args._display == 'italics' or args._display == 'italic' then formatFunc = italicize elseif args._display == 'plain' then formatFunc = plain else formatFunc = makeParam end -- Build the table of template arguments. local targs = {} for k, v in pairs(args) do if type(k) == 'number' then targs[v] = formatFunc(v) elseif not k:find('^_') then targs[k] = v end end --targs['nocat'] = 'yes'; --targs['categories'] = 'no'; --targs['demo'] = 'yes'; -- Find the template name. local template if args._template then template = args._template else local currentTitle = mw.title.getCurrentTitle() if currentTitle.prefixedText:find('/sandbox$') then template = currentTitle.prefixedText else template = currentTitle.basePageTitle.prefixedText end end -- Call the template with the arguments. frame = frame or mw.getCurrentFrame() local success, result = pcall( frame.expandTemplate, frame, {title = template, args = targs} ) if success then return result else return '' end end function p.main(frame) local args = require('Module:Arguments').getArgs(frame, { wrappers = 'Template:Parameter names example' }) return p._main(args, frame) end return p fdf94fb7a5dc1fabf118d60488a02f1e65b0df24 Help:Infobox/user style 12 493 985 984 2023-07-10T16:31:51Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{{heading| ==Infoboxes and user style == }}} Users can have [[WP:User style|user CSS]] that hides<!--, moves, or makes collapsible--> any infoboxes in their own browsers. To hide all infoboxes, add the following to [[Special:MyPage/common.css]] (for all [[WP:Skin|skins]], or [[Special:MyPage/skin.css]] for just the current skin), on a line by itself: <syntaxhighlight lang="css">div.mw-parser-output .infobox { display: none; }</syntaxhighlight> Alternatively, you can add the following code to [[Special:MyPage/common.js|your common.js]] or into a browser user script that is executed by an extension like [[Greasemonkey]]: <syntaxhighlight lang="js">$('.infobox').hide();</syntaxhighlight> Be aware that although{{#if:{{{guideline|}}}||, per [[WP:Manual of Style/Infoboxes]],}} all information in an infobox ideally should also be found in the main body of an article, there isn't perfect compliance with this guideline. For example, the full taxonomic hierarchy in {{tlx|Taxobox}}, and the OMIM and other medical database codes of {{tlx|Infobox disease}} are often not found in the main article content. The infobox is also often the location of the most significant, even only, image in an article. There is a userscript which removes infoboxes but moves the images contained to separate thumbnails: [[User:Maddy from Celeste/disinfobox.js]].<!-- Needs Special:Mypage/common.js options for: * Making infoboxes collapsible ** Making them auto-collapsed * Moving infoboxes to bottom of page --><noinclude> {{Documentation|content= This documentation snippet is transcluded at [[Help:Infobox]], [[Template:Infobox/doc]], [[WP:Customisation#Hiding specific messages]], [[Help:User style]], [[WP:Manual of Style/Infoboxes]], and other places where this information is relevant. As a template, this snippet takes a {{para|heading}} parameter to replace the level-2 <code>==Infoboxes and user style==</code> section heading code, as needed. E.g., for a <code>=== ... ===</code> level-3 heading: <code><nowiki>heading={{=}}{{=}}{{=}}Infoboxes and user style{{=}}{{=}}{{=}}</nowiki></code> }} </noinclude> ba4dac68eb2bdc49a32f2a11b9afd52381bf06b5 MechAssault 0 494 989 2023-07-10T16:48:53Z BigTa1k 2 Created page with "Test" wikitext text/x-wiki Test 640ab2bae07bedc4c163f679a746f7ab7fb5d1fa Module:String2 828 495 991 990 2023-07-10T16:51:27Z BigTa1k 2 1 revision imported Scribunto text/plain local p = {} p.trim = function(frame) return mw.text.trim(frame.args[1] or "") end p.sentence = function (frame) -- {{lc:}} is strip-marker safe, string.lower is not. frame.args[1] = frame:callParserFunction('lc', frame.args[1]) return p.ucfirst(frame) end p.ucfirst = function (frame ) local s = mw.text.trim( frame.args[1] or "" ) local s1 = "" -- if it's a list chop off and (store as s1) everything up to the first <li> local lipos = mw.ustring.find(s, "<li>" ) if lipos then s1 = mw.ustring.sub(s, 1, lipos + 3) s = mw.ustring.sub(s, lipos + 4) end -- s1 is either "" or the first part of the list markup, so we can continue -- and prepend s1 to the returned string local letterpos if mw.ustring.find(s, "^%[%[[^|]+|[^%]]+%]%]") then -- this is a piped wikilink, so we capitalise the text, not the pipe local _ _, letterpos = mw.ustring.find(s, "|%W*%w") -- find the first letter after the pipe else letterpos = mw.ustring.find(s, '%w') end if letterpos then local first = mw.ustring.sub(s, 1, letterpos - 1) local letter = mw.ustring.sub(s, letterpos, letterpos) local rest = mw.ustring.sub(s, letterpos + 1) return s1 .. first .. mw.ustring.upper(letter) .. rest else return s1 .. s end end p.title = function (frame ) -- http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html -- recommended by The U.S. Government Printing Office Style Manual: -- "Capitalize all words in titles of publications and documents, -- except a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor." local alwayslower = {['a'] = 1, ['an'] = 1, ['the'] = 1, ['and'] = 1, ['but'] = 1, ['or'] = 1, ['for'] = 1, ['nor'] = 1, ['on'] = 1, ['in'] = 1, ['at'] = 1, ['to'] = 1, ['from'] = 1, ['by'] = 1, ['of'] = 1, ['up'] = 1 } local res = '' local s = mw.text.trim( frame.args[1] or "" ) local words = mw.text.split( s, " ") for i, s in ipairs(words) do -- {{lc:}} is strip-marker safe, string.lower is not. s = frame:callParserFunction('lc', s) if i == 1 or alwayslower[s] ~= 1 then s = mw.getContentLanguage():ucfirst(s) end words[i] = s end return table.concat(words, " ") end -- findlast finds the last item in a list -- the first unnamed parameter is the list -- the second, optional unnamed parameter is the list separator (default = comma space) -- returns the whole list if separator not found p.findlast = function(frame) local s = mw.text.trim( frame.args[1] or "" ) local sep = frame.args[2] or "" if sep == "" then sep = ", " end local pattern = ".*" .. sep .. "(.*)" local a, b, last = s:find(pattern) if a then return last else return s end end -- stripZeros finds the first number and strips leading zeros (apart from units) -- e.g "0940" -> "940"; "Year: 0023" -> "Year: 23"; "00.12" -> "0.12" p.stripZeros = function(frame) local s = mw.text.trim(frame.args[1] or "") local n = tonumber( string.match( s, "%d+" ) ) or "" s = string.gsub( s, "%d+", n, 1 ) return s end -- nowiki ensures that a string of text is treated by the MediaWiki software as just a string -- it takes an unnamed parameter and trims whitespace, then removes any wikicode p.nowiki = function(frame) local str = mw.text.trim(frame.args[1] or "") return mw.text.nowiki(str) end -- split splits text at boundaries specified by separator -- and returns the chunk for the index idx (starting at 1) -- #invoke:String2 |split |text |separator |index |true/false -- #invoke:String2 |split |txt=text |sep=separator |idx=index |plain=true/false -- if plain is false/no/0 then separator is treated as a Lua pattern - defaults to plain=true p.split = function(frame) local args = frame.args if not(args[1] or args.txt) then args = frame:getParent().args end local txt = args[1] or args.txt or "" if txt == "" then return nil end local sep = (args[2] or args.sep or ""):gsub('"', '') local idx = tonumber(args[3] or args.idx) or 1 local plain = (args[4] or args.plain or "true"):sub(1,1) plain = (plain ~= "f" and plain ~= "n" and plain ~= "0") local splittbl = mw.text.split( txt, sep, plain ) if idx < 0 then idx = #splittbl + idx + 1 end return splittbl[idx] end -- val2percent scans through a string, passed as either the first unnamed parameter or |txt= -- it converts each number it finds into a percentage and returns the resultant string. p.val2percent = function(frame) local args = frame.args if not(args[1] or args.txt) then args = frame:getParent().args end local txt = mw.text.trim(args[1] or args.txt or "") if txt == "" then return nil end local function v2p (x) x = (tonumber(x) or 0) * 100 if x == math.floor(x) then x = math.floor(x) end return x .. "%" end txt = txt:gsub("%d[%d%.]*", v2p) -- store just the string return txt end -- one2a scans through a string, passed as either the first unnamed parameter or |txt= -- it converts each occurrence of 'one ' into either 'a ' or 'an ' and returns the resultant string. p.one2a = function(frame) local args = frame.args if not(args[1] or args.txt) then args = frame:getParent().args end local txt = mw.text.trim(args[1] or args.txt or "") if txt == "" then return nil end txt = txt:gsub(" one ", " a "):gsub("^one", "a"):gsub("One ", "A "):gsub("a ([aeiou])", "an %1"):gsub("A ([aeiou])", "An %1") return txt end -- findpagetext returns the position of a piece of text in a page -- First positional parameter or |text is the search text -- Optional parameter |title is the page title, defaults to current page -- Optional parameter |plain is either true for plain search (default) or false for Lua pattern search -- Optional parameter |nomatch is the return value when no match is found; default is nil p._findpagetext = function(args) -- process parameters local nomatch = args.nomatch or "" if nomatch == "" then nomatch = nil end -- local text = mw.text.trim(args[1] or args.text or "") if text == "" then return nil end -- local title = args.title or "" local titleobj if title == "" then titleobj = mw.title.getCurrentTitle() else titleobj = mw.title.new(title) end -- local plain = args.plain or "" if plain:sub(1, 1) == "f" then plain = false else plain = true end -- get the page content and look for 'text' - return position or nomatch local content = titleobj and titleobj:getContent() return content and mw.ustring.find(content, text, 1, plain) or nomatch end p.findpagetext = function(frame) local args = frame.args local pargs = frame:getParent().args for k, v in pairs(pargs) do args[k] = v end if not (args[1] or args.text) then return nil end -- just the first value return (p._findpagetext(args)) end -- returns the decoded url. Inverse of parser function {{urlencode:val|TYPE}} -- Type is: -- QUERY decodes + to space (default) -- PATH does no extra decoding -- WIKI decodes _ to space p._urldecode = function(url, type) url = url or "" type = (type == "PATH" or type == "WIKI") and type return mw.uri.decode( url, type ) end -- {{#invoke:String2|urldecode|url=url|type=type}} p.urldecode = function(frame) return mw.uri.decode( frame.args.url, frame.args.type ) end -- what follows was merged from Module:StringFunc -- helper functions p._GetParameters = require('Module:GetParameters') -- Argument list helper function, as per Module:String p._getParameters = p._GetParameters.getParameters -- Escape Pattern helper function so that all characters are treated as plain text, as per Module:String function p._escapePattern( pattern_str) return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ) end -- Helper Function to interpret boolean strings, as per Module:String p._getBoolean = p._GetParameters.getBoolean --[[ Strip This function Strips characters from string Usage: {{#invoke:String2|strip|source_string|characters_to_strip|plain_flag}} Parameters source: The string to strip chars: The pattern or list of characters to strip from string, replaced with '' plain: A flag indicating that the chars should be understood as plain text. defaults to true. Leading and trailing whitespace is also automatically stripped from the string. ]] function p.strip( frame ) local new_args = p._getParameters( frame.args, {'source', 'chars', 'plain'} ) local source_str = new_args['source'] or '' local chars = new_args['chars'] or '' or 'characters' source_str = mw.text.trim(source_str) if source_str == '' or chars == '' then return source_str end local l_plain = p._getBoolean( new_args['plain'] or true ) if l_plain then chars = p._escapePattern( chars ) end local result result = mw.ustring.gsub(source_str, "["..chars.."]", '') return result end --[[ Match any Returns the index of the first given pattern to match the input. Patterns must be consecutively numbered. Returns the empty string if nothing matches for use in {{#if:}} Usage: {{#invoke:String2|matchAll|source=123 abc|456|abc}} returns '2'. Parameters: source: the string to search plain: A flag indicating that the patterns should be understood as plain text. defaults to true. 1, 2, 3, ...: the patterns to search for ]] function p.matchAny(frame) local source_str = frame.args['source'] or error('The source parameter is mandatory.') local l_plain = p._getBoolean( frame.args['plain'] or true ) for i = 1, math.huge do local pattern = frame.args[i] if not pattern then return '' end if mw.ustring.find(source_str, pattern, 1, l_plain) then return tostring(i) end end end --[[--------------------------< H Y P H E N _ T O _ D A S H >-------------------------------------------------- Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are returned unmodified. These forms are modified: letter - letter (A - B) digit - digit (4-5) digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d) any other forms are returned unmodified. str may be a comma- or semicolon-separated list ]] function p.hyphen_to_dash( str, spacing ) if (str == nil or str == '') then return str end local accept str = mw.text.decode(str, true ) -- replace html entities with their characters; semicolon mucks up the text.split local out = {} local list = mw.text.split (str, '%s*[,;]%s*') -- split str at comma or semicolon separators if there are any for _, item in ipairs (list) do -- for each item in the list item = mw.text.trim(item) -- trim whitespace item, accept = item:gsub ('^%(%((.+)%)%)$', '%1') if accept == 0 and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit) item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter) item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or -- digit separator digit hyphen digit separator digit item:match ('^%d+%s*%-%s*%d+$') or -- digit hyphen digit item:match ('^%a+%s*%-%s*%a+$') then -- letter hyphen letter item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2') -- replace hyphen, remove extraneous space characters else item = mw.ustring.gsub (item, '%s*[–—]%s*', '–') -- for endash or emdash separated ranges, replace em with en, remove extraneous whitespace end end table.insert (out, item) -- add the (possibly modified) item to the output table end local temp_str = table.concat (out, ',' .. spacing) -- concatenate the output table into a comma separated string temp_str, accept = temp_str:gsub ('^%(%((.+)%)%)$', '%1') -- remove accept-this-as-written markup when it wraps all of concatenated out if accept ~= 0 then temp_str = str:gsub ('^%(%((.+)%)%)$', '%1') -- when global markup removed, return original str; do it this way to suppress boolean second return value end return temp_str end function p.hyphen2dash( frame ) local str = frame.args[1] or '' local spacing = frame.args[2] or ' ' -- space is part of the standard separator for normal spacing (but in conjunction with templates r/rp/ran we may need a narrower spacing return p.hyphen_to_dash(str, spacing) end -- Similar to [[Module:String#endswith]] function p.startswith(frame) return (frame.args[1]:sub(1, frame.args[2]:len()) == frame.args[2]) and 'yes' or '' end return p 418f407ee2454811910e333a13523afc975fa40c Module:Convert 828 496 993 992 2023-07-10T16:51:30Z BigTa1k 2 1 revision imported Scribunto text/plain -- Convert a value from one unit of measurement to another. -- Example: {{convert|123|lb|kg}} --> 123 pounds (56 kg) -- See [[:en:Template:Convert/Transwiki guide]] if copying to another wiki. local MINUS = '−' -- Unicode U+2212 MINUS SIGN (UTF-8: e2 88 92) local abs = math.abs local floor = math.floor local format = string.format local log10 = math.log10 local ustring = mw.ustring local ulen = ustring.len local usub = ustring.sub -- Configuration options to keep magic values in one location. -- Conversion data and message text are defined in separate modules. local config, maxsigfig local numdot -- must be '.' or ',' or a character which works in a regex local numsep, numsep_remove, numsep_remove2 local data_code, all_units local text_code local varname -- can be a code to use variable names that depend on value local from_en_table -- to translate an output string of en digits to local language local to_en_table -- to translate an input string of digits in local language to en -- Use translation_table in convert/text to change the following. local en_default -- true uses lang=en unless convert has lang=local or local digits local group_method = 3 -- code for how many digits are in a group local per_word = 'per' -- for units like "liters per kilometer" local plural_suffix = 's' -- only other useful value is probably '' to disable plural unit names local omitsep -- true to omit separator before local symbol/name -- All units should be defined in the data module. However, to cater for quick changes -- and experiments, any unknown unit is looked up in an extra data module, if it exists. -- That module would be transcluded in only a small number of pages, so there should be -- little server overhead from making changes, and changes should propagate quickly. local extra_module -- name of module with extra units local extra_units -- nil or table of extra units from extra_module -- Some options in the invoking template can set variables used later in the module. local currency_text -- for a user-defined currency symbol: {{convert|12|$/ha|$=€}} (euro replaces dollar) local function from_en(text) -- Input is a string representing a number in en digits with '.' decimal mark, -- without digit grouping (which is done just after calling this). -- Return the translation of the string with numdot and digits in local language. if numdot ~= '.' then text = text:gsub('%.', numdot) end if from_en_table then text = text:gsub('%d', from_en_table) end return text end local function to_en(text) -- Input is a string representing a number in the local language with -- an optional numdot decimal mark and numsep digit grouping. -- Return the translation of the string with '.' mark and en digits, -- and no separators (they have to be removed here to handle cases like -- numsep = '.' and numdot = ',' with input "1.234.567,8"). if to_en_table then text = ustring.gsub(text, '%d', to_en_table) end if numsep_remove then text = text:gsub(numsep_remove, '') end if numsep_remove2 then text = text:gsub(numsep_remove2, '') end if numdot ~= '.' then text = text:gsub(numdot, '.') end return text end local function decimal_mark(text) -- Return ',' if text probably is using comma for decimal mark, or has no decimal mark. -- Return '.' if text probably is using dot for decimal mark. -- Otherwise return nothing (decimal mark not known). if not text:find('[.,]') then return ',' end text = text:gsub('^%-', ''):gsub('%+%d+/%d+$', ''):gsub('[Ee]%-?%d+$', '') local decimal = text:match('^0?([.,])%d+$') or text:match('%d([.,])%d?%d?$') or text:match('%d([.,])%d%d%d%d+$') if decimal then return decimal end if text:match('%.%d+%.') then return ',' end if text:match('%,%d+,') then return '.' end end local add_warning, with_separator -- forward declarations local function to_en_with_check(text, parms) -- Version of to_en() for a wiki using numdot = ',' and numsep = '.' to check -- text (an input number as a string) which might have been copied from enwiki. -- For example, in '1.234' the '.' could be a decimal mark or a group separator. -- From viwiki. if to_en_table then text = ustring.gsub(text, '%d', to_en_table) end if decimal_mark(text) == '.' then local original = text text = text:gsub(',', '') -- for example, interpret "1,234.5" as an enwiki value if parms then add_warning(parms, 0, 'cvt_enwiki_num', original, with_separator({}, text)) end else if numsep_remove then text = text:gsub(numsep_remove, '') end if numsep_remove2 then text = text:gsub(numsep_remove2, '') end if numdot ~= '.' then text = text:gsub(numdot, '.') end end return text end local function omit_separator(id) -- Return true if there should be no separator before id (a unit symbol or name). -- For zhwiki, there should be no separator if id uses local characters. -- The following kludge should be a sufficient test. if omitsep then if id:sub(1, 2) == '-{' then -- for "-{...}-" content language variant return true end if id:byte() > 127 then local first = usub(id, 1, 1) if first ~= 'Å' and first ~= '°' and first ~= 'µ' then return true end end end return id:sub(1, 1) == '/' -- no separator before units like "/ha" end local spell_module -- name of module that can spell numbers local speller -- function from that module to handle spelling (set if needed) local wikidata_module, wikidata_data_module -- names of Wikidata modules local wikidata_code, wikidata_data -- exported tables from those modules (set if needed) local function set_config(args) -- Set configuration options from template #invoke or defaults. config = args maxsigfig = config.maxsigfig or 14 -- maximum number of significant figures local data_module, text_module local sandbox = config.sandbox and ('/' .. config.sandbox) or '' data_module = "Module:Convert/data" .. sandbox text_module = "Module:Convert/text" .. sandbox extra_module = "Module:Convert/extra" .. sandbox wikidata_module = "Module:Convert/wikidata" .. sandbox wikidata_data_module = "Module:Convert/wikidata/data" .. sandbox spell_module = "Module:ConvertNumeric" data_code = mw.loadData(data_module) text_code = mw.loadData(text_module) all_units = data_code.all_units local translation = text_code.translation_table if translation then numdot = translation.numdot numsep = translation.numsep if numdot == ',' and numsep == '.' then if text_code.all_messages.cvt_enwiki_num then to_en = to_en_with_check end end if translation.group then group_method = translation.group end if translation.per_word then per_word = translation.per_word end if translation.plural_suffix then plural_suffix = translation.plural_suffix end varname = translation.varname from_en_table = translation.from_en local use_workaround = true if use_workaround then -- 2013-07-05 workaround bug by making a copy of the required table. -- mw.ustring.gsub fails with a table (to_en_table) as the replacement, -- if the table is accessed via mw.loadData. local source = translation.to_en if source then to_en_table = {} for k, v in pairs(source) do to_en_table[k] = v end end else to_en_table = translation.to_en end if translation.lang == 'en default' then en_default = true -- for hiwiki end omitsep = translation.omitsep -- for zhwiki end numdot = config.numdot or numdot or '.' -- decimal mark before fractional digits numsep = config.numsep or numsep or ',' -- group separator for numbers -- numsep should be ',' or '.' or '' or '&nbsp;' or a Unicode character. -- numsep_remove must work in a regex to identify separators to be removed. if numsep ~= '' then numsep_remove = (numsep == '.') and '%.' or numsep end if numsep ~= ',' and numdot ~= ',' then numsep_remove2 = ',' -- so numbers copied from enwiki will work end end local function collection() -- Return a table to hold items. return { n = 0, add = function (self, item) self.n = self.n + 1 self[self.n] = item end, } end local function divide(numerator, denominator) -- Return integers quotient, remainder resulting from dividing the two -- given numbers, which should be unsigned integers. local quotient, remainder = floor(numerator / denominator), numerator % denominator if not (0 <= remainder and remainder < denominator) then -- Floating point limits may need this, as in {{convert|160.02|Ym|ydftin}}. remainder = 0 end return quotient, remainder end local function split(text, delimiter) -- Return a numbered table with fields from splitting text. -- The delimiter is used in a regex without escaping (for example, '.' would fail). -- Each field has any leading/trailing whitespace removed. local t = {} text = text .. delimiter -- to get last item for item in text:gmatch('%s*(.-)%s*' .. delimiter) do table.insert(t, item) end return t end local function strip(text) -- If text is a string, return its content with no leading/trailing -- whitespace. Otherwise return nil (a nil argument gives a nil result). if type(text) == 'string' then return text:match("^%s*(.-)%s*$") end end local function table_len(t) -- Return length (<100) of a numbered table to replace #t which is -- documented to not work if t is accessed via mw.loadData(). for i = 1, 100 do if t[i] == nil then return i - 1 end end end local function wanted_category(catkey, catsort, want_warning) -- Return message category if it is wanted in current namespace, -- otherwise return ''. local cat local title = mw.title.getCurrentTitle() if title then local nsdefault = '0' -- default namespace: '0' = article; '0,10' = article and template local namespace = title.namespace for _, v in ipairs(split(config.nscat or nsdefault, ',')) do if namespace == tonumber(v) then cat = text_code.all_categories[want_warning and 'warning' or catkey] if catsort and catsort ~= '' and cat:sub(-2) == ']]' then cat = cat:sub(1, -3) .. '|' .. mw.text.nowiki(usub(catsort, 1, 20)) .. ']]' end break end end end return cat or '' end local function message(parms, mcode, is_warning) -- Return wikitext for an error message, including category if specified -- for the message type. -- mcode = numbered table specifying the message: -- mcode[1] = 'cvt_xxx' (string used as a key to get message info) -- mcode[2] = 'parm1' (string to replace '$1' if any in message) -- mcode[3] = 'parm2' (string to replace '$2' if any in message) -- mcode[4] = 'parm3' (string to replace '$3' if any in message) local msg if type(mcode) == 'table' then if mcode[1] == 'cvt_no_output' then -- Some errors should cause convert to output an empty string, -- for example, for an optional field in an infobox. return '' end msg = text_code.all_messages[mcode[1]] end parms.have_problem = true local function subparm(fmt, ...) local rep = {} for i, v in ipairs({...}) do rep['$' .. i] = v end return (fmt:gsub('$%d+', rep)) end if msg then local parts = {} local regex, replace = msg.regex, msg.replace for i = 1, 3 do local limit = 40 local s = mcode[i + 1] if s then if regex and replace then s = s:gsub(regex, replace) limit = nil -- allow long "should be" messages end -- Escape user input so it does not break the message. -- To avoid tags (like {{convert|1<math>23</math>|m}}) breaking -- the mouseover title, any strip marker starting with char(127) is -- replaced with '...' (text not needing i18n). local append local pos = s:find(string.char(127), 1, true) if pos then append = '...' s = s:sub(1, pos - 1) end if limit and ulen(s) > limit then s = usub(s, 1, limit) append = '...' end s = mw.text.nowiki(s) .. (append or '') else s = '?' end parts['$' .. i] = s end local function ispreview() -- Return true if a prominent message should be shown. if parms.test == 'preview' or parms.test == 'nopreview' then -- For testing, can preview a real message or simulate a preview -- when running automated tests. return parms.test == 'preview' end local success, revid = pcall(function () return (parms.frame):preprocess('{{REVISIONID}}') end) return success and (revid == '') end local want_warning = is_warning and not config.warnings and -- show unobtrusive warnings if config.warnings not configured not msg.nowarn -- but use msg settings, not standard warning, if specified local title = string.gsub(msg[1] or 'Missing message', '$%d+', parts) local text = want_warning and '*' or msg[2] or 'Missing message' local cat = wanted_category(msg[3], mcode[2], want_warning) local anchor = msg[4] or '' local fmtkey = ispreview() and 'cvt_format_preview' or (want_warning and 'cvt_format2' or msg.format or 'cvt_format') local fmt = text_code.all_messages[fmtkey] or 'convert: bug' return subparm(fmt, title:gsub('"', '&quot;'), text, cat, anchor) end return 'Convert internal error: unknown message' end function add_warning(parms, level, key, text1, text2) -- for forward declaration above -- If enabled, add a warning that will be displayed after the convert result. -- A higher level is more verbose: more kinds of warnings are displayed. -- To reduce output noise, only the first warning is displayed. if level <= (tonumber(config.warnings) or 1) then if parms.warnings == nil then parms.warnings = message(parms, { key, text1, text2 }, true) end end end local function spell_number(parms, inout, number, numerator, denominator) -- Return result of spelling (number, numerator, denominator), or -- return nil if spelling is not available or not supported for given text. -- Examples (each value must be a string or nil): -- number numerator denominator output -- ------ --------- ----------- ------------------- -- "1.23" nil nil one point two three -- "1" "2" "3" one and two thirds -- nil "2" "3" two thirds if not speller then local function get_speller(module) return require(module).spell_number end local success success, speller = pcall(get_speller, spell_module) if not success or type(speller) ~= 'function' then add_warning(parms, 1, 'cvt_no_spell', 'spell') return nil end end local case if parms.spell_upper == inout then case = true parms.spell_upper = nil -- only uppercase first word in a multiple unit end local sp = not parms.opt_sp_us local adj = parms.opt_adjectival return speller(number, numerator, denominator, case, sp, adj) end ------------------------------------------------------------------------ -- BEGIN: Code required only for built-in units. -- LATER: If need much more code, move to another module to simplify this module. local function speed_of_sound(altitude) -- This is for the Mach built-in unit of speed. -- Return speed of sound in metres per second at given altitude in feet. -- If no altitude given, use default (zero altitude = sea level). -- Table gives speed of sound in miles per hour at various altitudes: -- altitude = -17,499 to 402,499 feet -- mach_table[a + 4] = s where -- a = (altitude / 5000) rounded to nearest integer (-3 to 80) -- s = speed of sound (mph) at that altitude -- LATER: Should calculate result from an interpolation between the next -- lower and higher altitudes in table, rather than rounding to nearest. -- From: http://www.aerospaceweb.org/question/atmosphere/q0112.shtml local mach_table = { -- a = 799.5, 787.0, 774.2, 761.207051, -- -3 to 0 748.0, 734.6, 721.0, 707.0, 692.8, 678.3, 663.5, 660.1, 660.1, 660.1, -- 1 to 10 660.1, 660.1, 660.1, 662.0, 664.3, 666.5, 668.9, 671.1, 673.4, 675.6, -- 11 to 20 677.9, 683.7, 689.9, 696.0, 702.1, 708.1, 714.0, 719.9, 725.8, 731.6, -- 21 to 30 737.3, 737.7, 737.7, 736.2, 730.5, 724.6, 718.8, 712.9, 707.0, 701.0, -- 31 to 40 695.0, 688.9, 682.8, 676.6, 670.4, 664.1, 657.8, 652.9, 648.3, 643.7, -- 41 to 50 639.1, 634.4, 629.6, 624.8, 620.0, 615.2, 613.2, 613.2, 613.2, 613.5, -- 51 to 60 614.4, 615.3, 616.7, 619.8, 623.4, 629.7, 635.0, 641.1, 650.6, 660.0, -- 61 to 70 672.5, 674.3, 676.1, 677.9, 679.7, 681.5, 683.3, 685.1, 686.8, 688.6, -- 71 to 80 } altitude = altitude or 0 local a = (altitude < 0) and -altitude or altitude a = floor(a / 5000 + 0.5) if altitude < 0 then a = -a end if a < -3 then a = -3 elseif a > 80 then a = 80 end return mach_table[a + 4] * 0.44704 -- mph converted to m/s end -- END: Code required only for built-in units. ------------------------------------------------------------------------ local function add_style(parms, class) -- Add selected template style to parms if not already present. parms.templatestyles = parms.templatestyles or {} if not parms.templatestyles[class] then parms.templatestyles[class] = parms.frame:extensionTag({ name = 'templatestyles', args = { src = text_code.titles[class] } }) end end local function get_styles(parms) -- Return string of required template styles, empty if none. if parms.templatestyles then local t = {} for _, v in pairs(parms.templatestyles) do table.insert(t, v) end return table.concat(t) end return '' end local function get_range(word) -- Return a range (string or table) corresponding to word (like "to"), -- or return nil if not a range word. local ranges = text_code.ranges return ranges.types[word] or ranges.types[ranges.aliases[word]] end local function check_mismatch(unit1, unit2) -- If unit1 cannot be converted to unit2, return an error message table. -- This allows conversion between units of the same type, and between -- Nm (normally torque) and ftlb (energy), as in gun-related articles. -- This works because Nm is the base unit (scale = 1) for both the -- primary type (torque), and the alternate type (energy, where Nm = J). -- A match occurs if the primary types are the same, or if unit1 matches -- the alternate type of unit2, and vice versa. That provides a whitelist -- of which conversions are permitted between normally incompatible types. if unit1.utype == unit2.utype or (unit1.utype == unit2.alttype and unit1.alttype == unit2.utype) then return nil end return { 'cvt_mismatch', unit1.utype, unit2.utype } end local function override_from(out_table, in_table, fields) -- Copy the specified fields from in_table to out_table, but do not -- copy nil fields (keep any corresponding field in out_table). for _, field in ipairs(fields) do if in_table[field] then out_table[field] = in_table[field] end end end local function shallow_copy(t) -- Return a shallow copy of table t. -- Do not need the features and overhead of the Scribunto mw.clone(). local result = {} for k, v in pairs(t) do result[k] = v end return result end local unit_mt = { -- Metatable to get missing values for a unit that does not accept SI prefixes. -- Warning: The boolean value 'false' is returned for any missing field -- so __index is not called twice for the same field in a given unit. __index = function (self, key) local value if key == 'name1' or key == 'sym_us' then value = self.symbol elseif key == 'name2' then value = self.name1 .. plural_suffix elseif key == 'name1_us' then value = self.name1 if not rawget(self, 'name2_us') then -- If name1_us is 'foot', do not make name2_us by appending plural_suffix. self.name2_us = self.name2 end elseif key == 'name2_us' then local raw1_us = rawget(self, 'name1_us') if raw1_us then value = raw1_us .. plural_suffix else value = self.name2 end elseif key == 'link' then value = self.name1 else value = false end rawset(self, key, value) return value end } local function prefixed_name(unit, name, index) -- Return unit name with SI prefix inserted at correct position. -- index = 1 (name1), 2 (name2), 3 (name1_us), 4 (name2_us). -- The position is a byte (not character) index, so use Lua's sub(). local pos = rawget(unit, 'prefix_position') if type(pos) == 'string' then pos = tonumber(split(pos, ',')[index]) end if pos then return name:sub(1, pos - 1) .. unit.si_name .. name:sub(pos) end return unit.si_name .. name end local unit_prefixed_mt = { -- Metatable to get missing values for a unit that accepts SI prefixes. -- Before use, fields si_name, si_prefix must be defined. -- The unit must define _symbol, _name1 and -- may define _sym_us, _name1_us, _name2_us -- (_sym_us, _name2_us may be defined for a language using sp=us -- to refer to a variant unrelated to U.S. units). __index = function (self, key) local value if key == 'symbol' then value = self.si_prefix .. self._symbol if value == 'l' then value = 'L' end elseif key == 'sym_us' then value = rawget(self, '_sym_us') if value then value = self.si_prefix .. value else value = self.symbol end elseif key == 'name1' then value = prefixed_name(self, self._name1, 1) elseif key == 'name2' then value = rawget(self, '_name2') if value then value = prefixed_name(self, value, 2) else value = self.name1 .. plural_suffix end elseif key == 'name1_us' then value = rawget(self, '_name1_us') if value then value = prefixed_name(self, value, 3) else value = self.name1 end elseif key == 'name2_us' then value = rawget(self, '_name2_us') if value then value = prefixed_name(self, value, 4) elseif rawget(self, '_name1_us') then value = self.name1_us .. plural_suffix else value = self.name2 end elseif key == 'link' then value = self.name1 else value = false end rawset(self, key, value) return value end } local unit_per_mt = { -- Metatable to get values for a per unit of form "x/y". -- This is never called to determine a unit name or link because per units -- are handled as a special case. -- Similarly, the default output is handled elsewhere, and for a symbol -- this is only called from get_default() for default_exceptions. __index = function (self, key) local value if key == 'symbol' then local per = self.per local unit1, unit2 = per[1], per[2] if unit1 then value = unit1[key] .. '/' .. unit2[key] else value = '/' .. unit2[key] end elseif key == 'sym_us' then value = self.symbol elseif key == 'scale' then local per = self.per local unit1, unit2 = per[1], per[2] value = (unit1 and unit1.scale or 1) * self.scalemultiplier / unit2.scale else value = false end rawset(self, key, value) return value end } local function make_per(unitcode, unit_table, ulookup) -- Return true, t where t is a per unit with unit codes expanded to unit tables, -- or return false, t where t is an error message table. local result = { unitcode = unitcode, utype = unit_table.utype, per = {} } override_from(result, unit_table, { 'invert', 'iscomplex', 'default', 'link', 'symbol', 'symlink' }) result.symbol_raw = (result.symbol or false) -- to distinguish between a defined exception and a metatable calculation local prefix for i, v in ipairs(unit_table.per) do if i == 1 and v == '' then -- First unit symbol can be empty; that gives a nil first unit table. elseif i == 1 and text_code.currency[v] then prefix = currency_text or v else local success, t = ulookup(v) if not success then return false, t end result.per[i] = t end end local multiplier = unit_table.multiplier if not result.utype then -- Creating an automatic per unit. local unit1 = result.per[1] local utype = (unit1 and unit1.utype or prefix or '') .. '/' .. result.per[2].utype local t = data_code.per_unit_fixups[utype] if t then if type(t) == 'table' then utype = t.utype or utype result.link = result.link or t.link multiplier = multiplier or t.multiplier else utype = t end end result.utype = utype end result.scalemultiplier = multiplier or 1 result.vprefix = prefix or false -- set to non-nil to avoid calling __index return true, setmetatable(result, unit_per_mt) end local function lookup(parms, unitcode, what, utable, fails, depth) -- Return true, t where t is a copy of the unit's converter table, -- or return false, t where t is an error message table. -- Parameter 'what' determines whether combination units are accepted: -- 'no_combination' : single unit only -- 'any_combination' : single unit or combination or output multiple -- 'only_multiple' : single unit or output multiple only -- Parameter unitcode is a symbol (like 'g'), with an optional SI prefix (like 'kg'). -- If, for example, 'kg' is in this table, that entry is used; -- otherwise the prefix ('k') is applied to the base unit ('g'). -- If unitcode is a known combination code (and if allowed by what), -- a table of output multiple unit tables is included in the result. -- For compatibility with the old template, an underscore in a unitcode is -- replaced with a space so usage like {{convert|350|board_feet}} works. -- Wikignomes may also put two spaces or "&nbsp;" in combinations, so -- replace underscore, "&nbsp;", and multiple spaces with a single space. utable = utable or parms.unittable or all_units fails = fails or {} depth = depth and depth + 1 or 1 if depth > 9 then -- There are ways to mistakenly define units which result in infinite -- recursion when lookup() is called. That gives a long delay and very -- confusing error messages, so the depth parameter is used as a guard. return false, { 'cvt_lookup', unitcode } end if unitcode == nil or unitcode == '' then return false, { 'cvt_no_unit' } end unitcode = unitcode:gsub('_', ' '):gsub('&nbsp;', ' '):gsub(' +', ' ') local function call_make_per(t) return make_per(unitcode, t, function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end ) end local t = utable[unitcode] if t then if t.shouldbe then return false, { 'cvt_should_be', t.shouldbe } end if t.sp_us then parms.opt_sp_us = true end local target = t.target -- nil, or unitcode is an alias for this target if target then local success, result = lookup(parms, target, what, utable, fails, depth) if not success then return false, result end override_from(result, t, { 'customary', 'default', 'link', 'symbol', 'symlink' }) local multiplier = t.multiplier if multiplier then result.multiplier = tostring(multiplier) result.scale = result.scale * multiplier end return true, result end if t.per then return call_make_per(t) end local combo = t.combination -- nil or a table of unitcodes if combo then local multiple = t.multiple if what == 'no_combination' or (what == 'only_multiple' and not multiple) then return false, { 'cvt_bad_unit', unitcode } end -- Recursively create a combination table containing the -- converter table of each unitcode. local result = { utype = t.utype, multiple = multiple, combination = {} } local cvt = result.combination for i, v in ipairs(combo) do local success, t = lookup(parms, v, multiple and 'no_combination' or 'only_multiple', utable, fails, depth) if not success then return false, t end cvt[i] = t end return true, result end local result = shallow_copy(t) result.unitcode = unitcode if result.prefixes then result.si_name = '' result.si_prefix = '' return true, setmetatable(result, unit_prefixed_mt) end return true, setmetatable(result, unit_mt) end local SIprefixes = text_code.SIprefixes for plen = SIprefixes[1] or 2, 1, -1 do -- Look for an SI prefix; should never occur with an alias. -- Check for longer prefix first ('dam' is decametre). -- SIprefixes[1] = prefix maximum #characters (as seen by mw.ustring.sub). local prefix = usub(unitcode, 1, plen) local si = SIprefixes[prefix] if si then local t = utable[usub(unitcode, plen+1)] if t and t.prefixes then local result = shallow_copy(t) result.unitcode = unitcode result.si_name = parms.opt_sp_us and si.name_us or si.name result.si_prefix = si.prefix or prefix result.scale = t.scale * 10 ^ (si.exponent * t.prefixes) return true, setmetatable(result, unit_prefixed_mt) end end end -- Accept user-defined combinations like "acre+m2+ha" or "acre m2 ha" for output. -- If '+' is used, each unit code can include a space, and any error is fatal. -- If ' ' is used and if each space-separated word is a unit code, it is a combo, -- but errors are not fatal so the unit code can be looked up as an extra unit. local err_is_fatal local combo = collection() if unitcode:find('+', 1, true) then err_is_fatal = true for item in (unitcode .. '+'):gmatch('%s*(.-)%s*%+') do if item ~= '' then combo:add(item) end end elseif unitcode:find('%s') then for item in unitcode:gmatch('%S+') do combo:add(item) end end if combo.n > 1 then local function lookup_combo() if what == 'no_combination' or what == 'only_multiple' then return false, { 'cvt_bad_unit', unitcode } end local result = { combination = {} } local cvt = result.combination for i, v in ipairs(combo) do local success, t = lookup(parms, v, 'only_multiple', utable, fails, depth) if not success then return false, t end if i == 1 then result.utype = t.utype else local mismatch = check_mismatch(result, t) if mismatch then return false, mismatch end end cvt[i] = t end return true, result end local success, result = lookup_combo() if success or err_is_fatal then return success, result end end -- Accept any unit with an engineering notation prefix like "e6cuft" -- (million cubic feet), but not chained prefixes like "e3e6cuft", -- and not if the unit is a combination or multiple, -- and not if the unit has an offset or is a built-in. -- Only en digits are accepted. local exponent, baseunit = unitcode:match('^e(%d+)(.*)') if exponent then local engscale = text_code.eng_scales[exponent] if engscale then local success, result = lookup(parms, baseunit, 'no_combination', utable, fails, depth) if success and not (result.offset or result.builtin or result.engscale) then result.unitcode = unitcode -- 'e6cuft' not 'cuft' result.defkey = unitcode -- key to lookup default exception result.engscale = engscale result.scale = result.scale * 10 ^ tonumber(exponent) return true, result end end end -- Look for x/y; split on right-most slash to get scale correct (x/y/z is x/y per z). local top, bottom = unitcode:match('^(.-)/([^/]+)$') if top and not unitcode:find('e%d') then -- If valid, create an automatic per unit for an "x/y" unit code. -- The unitcode must not include extraneous spaces. -- Engineering notation (apart from at start and which has been stripped before here), -- is not supported so do not make a per unit if find text like 'e3' in unitcode. local success, result = call_make_per({ per = {top, bottom} }) if success then return true, result end end if not parms.opt_ignore_error and not get_range(unitcode) then -- Want the "what links here" list for the extra_module to show only cases -- where an extra unit is used, so do not require it if invoked from {{val}} -- or if looking up a range word which cannot be a unit. if not extra_units then local success, extra = pcall(function () return require(extra_module).extra_units end) if success and type(extra) == 'table' then extra_units = extra end end if extra_units then -- A unit in one data table might refer to a unit in the other table, so -- switch between them, relying on fails or depth to terminate loops. if not fails[unitcode] then fails[unitcode] = true local other = (utable == all_units) and extra_units or all_units local success, result = lookup(parms, unitcode, what, other, fails, depth) if success then return true, result end end end end if to_en_table then -- At fawiki it is common to translate all digits so a unit like "km2" becomes "km۲". local en_code = ustring.gsub(unitcode, '%d', to_en_table) if en_code ~= unitcode then return lookup(parms, en_code, what, utable, fails, depth) end end return false, { 'cvt_unknown', unitcode } end local function valid_number(num) -- Return true if num is a valid number. -- In Scribunto (different from some standard Lua), when expressed as a string, -- overflow or other problems are indicated with text like "inf" or "nan" -- which are regarded as invalid here (each contains "n"). if type(num) == 'number' and tostring(num):find('n', 1, true) == nil then return true end end local function hyphenated(name, parts) -- Return a hyphenated form of given name (for adjectival usage). -- The name may be linked and the target of the link must not be changed. -- Hypothetical examples: -- [[long ton|ton]] → [[long ton|ton]] (no change) -- [[tonne|long ton]] → [[tonne|long-ton]] -- [[metric ton|long ton]] → [[metric ton|long-ton]] -- [[long ton]] → [[long ton|long-ton]] -- Input can also have multiple links in a single name like: -- [[United States customary units|U.S.]] [[US gallon|gallon]] -- [[mile]]s per [[United States customary units|U.S.]] [[quart]] -- [[long ton]]s per [[short ton]] -- Assume that links cannot be nested (never like "[[abc[[def]]ghi]]"). -- This uses a simple and efficient procedure that works for most cases. -- Some units (if used) would require more, and can later think about -- adding a method to handle exceptions. -- The procedure is to replace each space with a hyphen, but -- not a space after ')' [for "(pre-1954&nbsp;US) nautical mile"], and -- not spaces immediately before '(' or in '(...)' [for cases like -- "British thermal unit (ISO)" and "Calorie (International Steam Table)"]. if name:find(' ', 1, true) then if parts then local pos if name:sub(1, 1) == '(' then pos = name:find(')', 1, true) if pos then return name:sub(1, pos+1) .. name:sub(pos+2):gsub(' ', '-') end elseif name:sub(-1) == ')' then pos = name:find('(', 1, true) if pos then return name:sub(1, pos-2):gsub(' ', '-') .. name:sub(pos-1) end end return name:gsub(' ', '-') end parts = collection() for before, item, after in name:gmatch('([^[]*)(%[%[[^[]*%]%])([^[]*)') do if item:find(' ', 1, true) then local prefix local plen = item:find('|', 1, true) if plen then prefix = item:sub(1, plen) item = item:sub(plen + 1, -3) else prefix = item:sub(1, -3) .. '|' item = item:sub(3, -3) end item = prefix .. hyphenated(item, parts) .. ']]' end parts:add(before:gsub(' ', '-') .. item .. after:gsub(' ', '-')) end if parts.n == 0 then -- No link like "[[...]]" was found in the original name. parts:add(hyphenated(name, parts)) end return table.concat(parts) end return name end local function hyphenated_maybe(parms, want_name, sep, id, inout) -- Return s, f where -- s = id, possibly modified -- f = true if hyphenated -- Possible modifications: hyphenate; prepend '-'; append mid text. if id == nil or id == '' then return '' end local mid = (inout == (parms.opt_flip and 'out' or 'in')) and parms.mid or '' if want_name then if parms.opt_adjectival then return '-' .. hyphenated(id) .. mid, true end if parms.opt_add_s and id:sub(-1) ~= 's' then id = id .. 's' -- for nowiki end end return sep .. id .. mid end local function use_minus(text) -- Return text with Unicode minus instead of '-', if present. if text:sub(1, 1) == '-' then return MINUS .. text:sub(2) end return text end local function digit_groups(parms, text, method) -- Return a numbered table of groups of digits (left-to-right, in local language). -- Parameter method is a number or nil: -- 3 for 3-digit grouping (default), or -- 2 for 3-then-2 grouping (only for digits before decimal mark). local len_right local len_left = text:find('.', 1, true) if len_left then len_right = #text - len_left len_left = len_left - 1 else len_left = #text end local twos = method == 2 and len_left > 5 local groups = collection() local run = len_left local n if run < 4 or (run == 4 and parms.opt_comma5) then if parms.opt_gaps then n = run else n = #text end elseif twos then n = run % 2 == 0 and 1 or 2 else n = run % 3 == 0 and 3 or run % 3 end while run > 0 do groups:add(n) run = run - n n = (twos and run > 3) and 2 or 3 end if len_right then if groups.n == 0 then groups:add(0) end if parms.opt_gaps and len_right > 3 then local want4 = not parms.opt_gaps3 -- true gives no gap before trailing single digit local isfirst = true run = len_right while run > 0 do n = (want4 and run == 4) and 4 or (run > 3 and 3 or run) if isfirst then isfirst = false groups[groups.n] = groups[groups.n] + 1 + n else groups:add(n) end run = run - n end else groups[groups.n] = groups[groups.n] + 1 + len_right end end local pos = 1 for i, length in ipairs(groups) do groups[i] = from_en(text:sub(pos, pos + length - 1)) pos = pos + length end return groups end function with_separator(parms, text) -- for forward declaration above -- Input text is a number in en digits with optional '.' decimal mark. -- Return an equivalent, formatted for display: -- with a custom decimal mark instead of '.', if wanted -- with thousand separators inserted, if wanted -- digits in local language -- The given text is like '123' or '123.' or '12345.6789'. -- The text has no sign (caller inserts that later, if necessary). -- When using gaps, they are inserted before and after the decimal mark. -- Separators are inserted only before the decimal mark. -- A trailing dot (as in '123.') is removed because their use appears to -- be accidental, and such a number should be shown as '123' or '123.0'. -- It is useful for convert to suppress the dot so, for example, '4000.' -- is a simple way of indicating that all the digits are significant. if text:sub(-1) == '.' then text = text:sub(1, -2) end if #text < 4 or parms.opt_nocomma or numsep == '' then return from_en(text) end local groups = digit_groups(parms, text, group_method) if parms.opt_gaps then if groups.n <= 1 then return groups[1] or '' end local nowrap = '<span style="white-space: nowrap">' local gap = '<span style="margin-left: 0.25em">' local close = '</span>' return nowrap .. groups[1] .. gap .. table.concat(groups, close .. gap, 2, groups.n) .. close .. close end return table.concat(groups, numsep) end -- An input value like 1.23e12 is displayed using scientific notation (1.23×10¹²). -- That also makes the output use scientific notation, except for small values. -- In addition, very small or very large output values use scientific notation. -- Use format(fmtpower, significand, '10', exponent) where each argument is a string. local fmtpower = '%s<span style="margin:0 .15em 0 .25em">×</span>%s<sup>%s</sup>' local function with_exponent(parms, show, exponent) -- Return wikitext to display the implied value in scientific notation. -- Input uses en digits; output uses digits in local language. return format(fmtpower, with_separator(parms, show), from_en('10'), use_minus(from_en(tostring(exponent)))) end local function make_sigfig(value, sigfig) -- Return show, exponent that are equivalent to the result of -- converting the number 'value' (where value >= 0) to a string, -- rounded to 'sigfig' significant figures. -- The returned items are: -- show: a string of digits; no sign and no dot; -- there is an implied dot before show. -- exponent: a number (an integer) to shift the implied dot. -- Resulting value = tonumber('.' .. show) * 10^exponent. -- Examples: -- make_sigfig(23.456, 3) returns '235', 2 (.235 * 10^2). -- make_sigfig(0.0023456, 3) returns '235', -2 (.235 * 10^-2). -- make_sigfig(0, 3) returns '000', 1 (.000 * 10^1). if sigfig <= 0 then sigfig = 1 elseif sigfig > maxsigfig then sigfig = maxsigfig end if value == 0 then return string.rep('0', sigfig), 1 end local exp, fracpart = math.modf(log10(value)) if fracpart >= 0 then fracpart = fracpart - 1 exp = exp + 1 end local digits = format('%.0f', 10^(fracpart + sigfig)) if #digits > sigfig then -- Overflow (for sigfig=3: like 0.9999 rounding to "1000"; need "100"). digits = digits:sub(1, sigfig) exp = exp + 1 end assert(#digits == sigfig, 'Bug: rounded number has wrong length') return digits, exp end -- Fraction output format. local fracfmt = { { -- Like {{frac}} (fraction slash). '<span class="frac" role="math">{SIGN}<span class="num">{NUM}</span>&frasl;<span class="den">{DEN}</span></span>', -- 1/2 '<span class="frac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="num">{NUM}</span>&frasl;<span class="den">{DEN}</span></span>', -- 1+2/3 style = 'frac', }, { -- Like {{sfrac}} (stacked fraction, that is, horizontal bar). '<span class="sfrac tion" role="math">{SIGN}<span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span>', -- 1//2 '<span class="sfrac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="tion"><span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span></span>', -- 1+2//3 style = 'sfrac', }, } local function format_fraction(parms, inout, negative, wholestr, numstr, denstr, do_spell, style) -- Return wikitext for a fraction, possibly spelled. -- Inputs use en digits and have no sign; output uses digits in local language. local wikitext if not style then style = parms.opt_fraction_horizontal and 2 or 1 end if wholestr == '' then wholestr = nil end local substitute = { SIGN = negative and MINUS or '', WHOLE = wholestr and with_separator(parms, wholestr), NUM = from_en(numstr), DEN = from_en(denstr), } wikitext = fracfmt[style][wholestr and 2 or 1]:gsub('{(%u+)}', substitute) if do_spell then if negative then if wholestr then wholestr = '-' .. wholestr else numstr = '-' .. numstr end end local s = spell_number(parms, inout, wholestr, numstr, denstr) if s then return s end end add_style(parms, fracfmt[style].style) return wikitext end local function format_number(parms, show, exponent, isnegative) -- Parameter show is a string or a table containing strings. -- Each string is a formatted number in en digits and optional '.' decimal mark. -- A table represents a fraction: integer, numerator, denominator; -- if a table is given, exponent must be nil. -- Return t where t is a table with fields: -- show = wikitext formatted to display implied value -- (digits in local language) -- is_scientific = true if show uses scientific notation -- clean = unformatted show (possibly adjusted and with inserted '.') -- (en digits) -- sign = '' or MINUS -- exponent = exponent (possibly adjusted) -- The clean and exponent fields can be used to calculate the -- rounded absolute value, if needed. -- -- The value implied by the arguments is found from: -- exponent is nil; and -- show is a string of digits (no sign), with an optional dot; -- show = '123.4' is value 123.4, '1234' is value 1234.0; -- or: -- exponent is an integer indicating where dot should be; -- show is a string of digits (no sign and no dot); -- there is an implied dot before show; -- show does not start with '0'; -- show = '1234', exponent = 3 is value 0.1234*10^3 = 123.4. -- -- The formatted result: -- * Is for an output value and is spelled if wanted and possible. -- * Includes a Unicode minus if isnegative and not spelled. -- * Uses a custom decimal mark, if wanted. -- * Has digits grouped where necessary, if wanted. -- * Uses scientific notation if requested, or for very small or large values -- (which forces result to not be spelled). -- * Has no more than maxsigfig significant digits -- (same as old template and {{#expr}}). local xhi, xlo -- these control when scientific notation (exponent) is used if parms.opt_scientific then xhi, xlo = 4, 2 -- default for output if input uses e-notation elseif parms.opt_scientific_always then xhi, xlo = 0, 0 -- always use scientific notation (experimental) else xhi, xlo = 10, 4 -- default end local sign = isnegative and MINUS or '' local maxlen = maxsigfig local tfrac if type(show) == 'table' then tfrac = show show = tfrac.wholestr assert(exponent == nil, 'Bug: exponent given with fraction') end if not tfrac and not exponent then local integer, dot, decimals = show:match('^(%d*)(%.?)(.*)') if integer == '0' or integer == '' then local zeros, figs = decimals:match('^(0*)([^0]?.*)') if #figs == 0 then if #zeros > maxlen then show = '0.' .. zeros:sub(1, maxlen) end elseif #zeros >= xlo then show = figs exponent = -#zeros elseif #figs > maxlen then show = '0.' .. zeros .. figs:sub(1, maxlen) end elseif #integer >= xhi then show = integer .. decimals exponent = #integer else maxlen = maxlen + #dot if #show > maxlen then show = show:sub(1, maxlen) end end end if exponent then local function zeros(n) return string.rep('0', n) end if #show > maxlen then show = show:sub(1, maxlen) end if exponent > xhi or exponent <= -xlo or (exponent == xhi and show ~= '1' .. zeros(xhi - 1)) then -- When xhi, xlo = 10, 4 (the default), scientific notation is used if the -- rounded value satisfies: value >= 1e9 or value < 1e-4 (1e9 = 0.1e10), -- except if show is '1000000000' (1e9), for example: -- {{convert|1000000000|m|m|sigfig=10}} → 1,000,000,000 metres (1,000,000,000 m) local significand if #show > 1 then significand = show:sub(1, 1) .. '.' .. show:sub(2) else significand = show end return { clean = '.' .. show, exponent = exponent, sign = sign, show = sign .. with_exponent(parms, significand, exponent-1), is_scientific = true, } end if exponent >= #show then show = show .. zeros(exponent - #show) -- result has no dot elseif exponent <= 0 then show = '0.' .. zeros(-exponent) .. show else show = show:sub(1, exponent) .. '.' .. show:sub(exponent+1) end end local formatted_show if tfrac then show = tostring(tfrac.value) -- to set clean in returned table formatted_show = format_fraction(parms, 'out', isnegative, tfrac.wholestr, tfrac.numstr, tfrac.denstr, parms.opt_spell_out) else if isnegative and show:match('^0.?0*$') then sign = '' -- don't show minus if result is negative but rounds to zero end formatted_show = sign .. with_separator(parms, show) if parms.opt_spell_out then formatted_show = spell_number(parms, 'out', sign .. show) or formatted_show end end return { clean = show, sign = sign, show = formatted_show, is_scientific = false, -- to avoid calling __index } end local function extract_fraction(parms, text, negative) -- If text represents a fraction, return -- value, altvalue, show, denominator -- where -- value is a number (value of the fraction in argument text) -- altvalue is an alternate interpretation of any fraction for the hands -- unit where "12.1+3/4" means 12 hands 1.75 inches -- show is a string (formatted text for display of an input value, -- and is spelled if wanted and possible) -- denominator is value of the denominator in the fraction -- Otherwise, return nil. -- Input uses en digits and '.' decimal mark (input has been translated). -- Output uses digits in local language and local decimal mark, if any. ------------------------------------------------------------------------ -- Originally this function accepted x+y/z where x, y, z were any valid -- numbers, possibly with a sign. For example '1.23e+2+1.2/2.4' = 123.5, -- and '2-3/8' = 1.625. However, such usages were found to be errors or -- misunderstandings, so since August 2014 the following restrictions apply: -- x (if present) is an integer or has a single digit after decimal mark -- y and z are unsigned integers -- e-notation is not accepted -- The overall number can start with '+' or '-' (so '12+3/4' and '+12+3/4' -- and '-12-3/4' are valid). -- Any leading negative sign is removed by the caller, so only inputs -- like the following are accepted here (may have whitespace): -- negative = false false true (there was a leading '-') -- text = '2/3' '+2/3' '2/3' -- text = '1+2/3' '+1+2/3' '1-2/3' -- text = '12.3+1/2' '+12.3+1/2' '12.3-1/2' -- Values like '12.3+1/2' are accepted, but are intended only for use -- with the hands unit (not worth adding code to enforce that). ------------------------------------------------------------------------ local leading_plus, prefix, numstr, slashes, denstr = text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*(/+)%s*(%d+)%s*$') if not leading_plus then -- Accept a single U+2044 fraction slash because that may be pasted. leading_plus, prefix, numstr, denstr = text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*⁄%s*(%d+)%s*$') slashes = '/' end local numerator = tonumber(numstr) local denominator = tonumber(denstr) if numerator == nil or denominator == nil or (negative and leading_plus ~= '') then return nil end local whole, wholestr if prefix == '' then wholestr = '' whole = 0 else -- Any prefix must be like '12+' or '12-' (whole number and fraction sign); -- '12.3+' and '12.3-' are also accepted (single digit after decimal point) -- because '12.3+1/2 hands' is valid (12 hands 3½ inches). local num1, num2, frac_sign = prefix:match('^(%d+)(%.?%d?)%s*([+%-])$') if num1 == nil then return nil end if num2 == '' then -- num2 must be '' or like '.1' but not '.' or '.12' wholestr = num1 else if #num2 ~= 2 then return nil end wholestr = num1 .. num2 end if frac_sign ~= (negative and '-' or '+') then return nil end whole = tonumber(wholestr) if whole == nil then return nil end end local value = whole + numerator / denominator if not valid_number(value) then return nil end local altvalue = whole + numerator / (denominator * 10) local style = #slashes -- kludge: 1 or 2 slashes can be used to select style if style > 2 then style = 2 end local wikitext = format_fraction(parms, 'in', negative, leading_plus .. wholestr, numstr, denstr, parms.opt_spell_in, style) return value, altvalue, wikitext, denominator end local function extract_number(parms, text, another, no_fraction) -- Return true, info if can extract a number from text, -- where info is a table with the result, -- or return false, t where t is an error message table. -- Input can use en digits or digits in local language and can -- have references at the end. Accepting references is intended -- for use in infoboxes with a field for a value passed to convert. -- Parameter another = true if the expected value is not the first. -- Before processing, the input text is cleaned: -- * Any thousand separators (valid or not) are removed. -- * Any sign is replaced with '-' (if negative) or '' (otherwise). -- That replaces Unicode minus with '-'. -- If successful, the returned info table contains named fields: -- value = a valid number -- altvalue = a valid number, usually same as value but different -- if fraction used (for hands unit) -- singular = true if value is 1 or -1 (to use singular form of units) -- clean = cleaned text with any separators and sign removed -- (en digits and '.' decimal mark) -- show = text formatted for output, possibly with ref strip markers -- (digits in local language and custom decimal mark) -- The resulting show: -- * Is for an input value and is spelled if wanted and possible. -- * Has a rounded value, if wanted. -- * Has digits grouped where necessary, if wanted. -- * If negative, a Unicode minus is used; otherwise the sign is -- '+' (if the input text used '+'), or is '' (if no sign in input). text = strip(text or '') local reference local pos = text:find('\127', 1, true) if pos then local before = text:sub(1, pos - 1) local remainder = text:sub(pos) local refs = {} while #remainder > 0 do local ref, spaces ref, spaces, remainder = remainder:match('^(\127[^\127]*UNIQ[^\127]*%-ref[^\127]*\127)(%s*)(.*)') if ref then table.insert(refs, ref) else refs = {} break end end if #refs > 0 then text = strip(before) reference = table.concat(refs) end end local clean = to_en(text, parms) if clean == '' then return false, { another and 'cvt_no_num2' or 'cvt_no_num' } end local isnegative, propersign = false, '' -- most common case local singular, show, denominator local value = tonumber(clean) local altvalue if value then local sign = clean:sub(1, 1) if sign == '+' or sign == '-' then propersign = (sign == '+') and '+' or MINUS clean = clean:sub(2) end if value < 0 then isnegative = true value = -value end else local valstr for _, prefix in ipairs({ '-', MINUS, '&minus;' }) do -- Including '-' sets isnegative in case input is a fraction like '-2-3/4'. local plen = #prefix if clean:sub(1, plen) == prefix then valstr = clean:sub(plen + 1) if valstr:match('^%s') then -- "- 1" is invalid but "-1 - 1/2" is ok return false, { 'cvt_bad_num', text } end break end end if valstr then isnegative = true propersign = MINUS clean = valstr value = tonumber(clean) end if value == nil then if not no_fraction then value, altvalue, show, denominator = extract_fraction(parms, clean, isnegative) end if value == nil then return false, { 'cvt_bad_num', text } end if value <= 1 then singular = true -- for example, "½ mile" or "one half mile" (singular unit) end end end if not valid_number(value) then -- for example, "1e310" may overflow return false, { 'cvt_invalid_num' } end if show == nil then -- clean is a non-empty string with no spaces, and does not represent a fraction, -- and value = tonumber(clean) is a number >= 0. -- If the input uses e-notation, show will be displayed using a power of ten, but -- we use the number as given so it might not be normalized scientific notation. -- The input value is spelled if specified so any e-notation is ignored; -- that allows input like 2e6 to be spelled as "two million" which works -- because the spell module converts '2e6' to '2000000' before spelling. local function rounded(value, default, exponent) local precision = parms.opt_ri if precision then local fmt = '%.' .. format('%d', precision) .. 'f' local result = fmt:format(tonumber(value) + 2e-14) -- fudge for some common cases of bad rounding if not exponent then singular = (tonumber(result) == 1) end return result end return default end singular = (value == 1) local scientific local significand, exponent = clean:match('^([%d.]+)[Ee]([+%-]?%d+)') if significand then show = with_exponent(parms, rounded(significand, significand, exponent), exponent) scientific = true else show = with_separator(parms, rounded(value, clean)) end show = propersign .. show if parms.opt_spell_in then show = spell_number(parms, 'in', propersign .. rounded(value, clean)) or show scientific = false end if scientific then parms.opt_scientific = true end end if isnegative and (value ~= 0) then value = -value altvalue = -(altvalue or value) end return true, { value = value, altvalue = altvalue or value, singular = singular, clean = clean, show = show .. (reference or ''), denominator = denominator, } end local function get_number(text) -- Return v, f where: -- v = nil (text is not a number) -- or -- v = value of text (text is a number) -- f = true if value is an integer -- Input can use en digits or digits in local language or separators, -- but no Unicode minus, and no fraction. if text then local number = tonumber(to_en(text)) if number then local _, fracpart = math.modf(number) return number, (fracpart == 0) end end end local function gcd(a, b) -- Return the greatest common denominator for the given values, -- which are known to be positive integers. if a > b then a, b = b, a end if a <= 0 then return b end local r = b % a if r <= 0 then return a end if r == 1 then return 1 end return gcd(r, a) end local function fraction_table(value, denominator) -- Return value as a string or a table: -- * If result is a string, there is no fraction, and the result -- is value formatted as a string of en digits. -- * If result is a table, it represents a fraction with named fields: -- wholestr, numstr, denstr (strings of en digits for integer, numerator, denominator). -- The result is rounded to the nearest multiple of (1/denominator). -- If the multiple is zero, no fraction is included. -- No fraction is included if value is very large as the fraction would -- be unhelpful, particularly if scientific notation is required. -- Input value is a non-negative number. -- Input denominator is a positive integer for the desired fraction. if value <= 0 then return '0' end if denominator <= 0 or value > 1e8 then return format('%.2f', value) end local integer, decimals = math.modf(value) local numerator = floor((decimals * denominator) + 0.5 + 2e-14) -- add fudge for some common cases of bad rounding if numerator >= denominator then integer = integer + 1 numerator = 0 end local wholestr = tostring(integer) if numerator > 0 then local div = gcd(numerator, denominator) if div > 1 then numerator = numerator / div denominator = denominator / div end return { wholestr = (integer > 0) and wholestr or '', numstr = tostring(numerator), denstr = tostring(denominator), value = value, } end return wholestr end local function preunits(count, preunit1, preunit2) -- If count is 1: -- ignore preunit2 -- return p1 -- else: -- preunit1 is used for preunit2 if the latter is empty -- return p1, p2 -- where: -- p1 is text to insert before the input unit -- p2 is text to insert before the output unit -- p1 or p2 may be nil to mean "no preunit" -- Using '+' gives output like "5+ feet" (no space before, but space after). local function withspace(text, wantboth) -- Return text with space before and, if wantboth, after. -- However, no space is added if there is a space or '&nbsp;' or '-' -- at that position ('-' is for adjectival text). -- There is also no space if text starts with '&' -- (e.g. '&deg;' would display a degree symbol with no preceding space). local char = text:sub(1, 1) if char == '&' then return text -- an html entity can be used to specify the exact display end if not (char == ' ' or char == '-' or char == '+') then text = ' ' .. text end if wantboth then char = text:sub(-1, -1) if not (char == ' ' or char == '-' or text:sub(-6, -1) == '&nbsp;') then text = text .. ' ' end end return text end local PLUS = '+ ' preunit1 = preunit1 or '' local trim1 = strip(preunit1) if count == 1 then if trim1 == '' then return nil end if trim1 == '+' then return PLUS end return withspace(preunit1, true) end preunit1 = withspace(preunit1) preunit2 = preunit2 or '' local trim2 = strip(preunit2) if trim1 == '+' then if trim2 == '' or trim2 == '+' then return PLUS, PLUS end preunit1 = PLUS end if trim2 == '' then if trim1 == '' then return nil, nil end preunit2 = preunit1 elseif trim2 == '+' then preunit2 = PLUS elseif trim2 == '&#32;' then -- trick to make preunit2 empty preunit2 = nil else preunit2 = withspace(preunit2) end return preunit1, preunit2 end local function range_text(range, want_name, parms, before, after, inout, options) -- Return before .. rtext .. after -- where rtext is the text that separates two values in a range. local rtext, adj_text, exception options = options or {} if type(range) == 'table' then -- Table must specify range text for ('off' and 'on') or ('input' and 'output'), -- and may specify range text for 'adj=on', -- and may specify exception = true. rtext = range[want_name and 'off' or 'on'] or range[((inout == 'in') == (parms.opt_flip == true)) and 'output' or 'input'] adj_text = range['adj'] exception = range['exception'] else rtext = range end if parms.opt_adjectival then if want_name or (exception and parms.abbr_org == 'on') then rtext = adj_text or rtext:gsub(' ', '-'):gsub('&nbsp;', '-') end end if rtext == '–' and (options.spaced or after:sub(1, #MINUS) == MINUS) then rtext = '&nbsp;– ' end return before .. rtext .. after end local function get_composite(parms, iparm, in_unit_table) -- Look for a composite input unit. For example, {{convert|1|yd|2|ft|3|in}} -- would result in a call to this function with -- iparm = 3 (parms[iparm] = "2", just after the first unit) -- in_unit_table = (unit table for "yd"; contains value 1 for number of yards) -- Return true, iparm, unit where -- iparm = index just after the composite units (7 in above example) -- unit = composite unit table holding all input units, -- or return true if no composite unit is present in parms, -- or return false, t where t is an error message table. local default, subinfo local composite_units, count = { in_unit_table }, 1 local fixups = {} local total = in_unit_table.valinfo[1].value local subunit = in_unit_table while subunit.subdivs do -- subdivs is nil or a table of allowed subdivisions local subcode = strip(parms[iparm+1]) local subdiv = subunit.subdivs[subcode] or subunit.subdivs[(all_units[subcode] or {}).target] if not subdiv then break end local success success, subunit = lookup(parms, subcode, 'no_combination') if not success then return false, subunit end -- should never occur success, subinfo = extract_number(parms, parms[iparm]) if not success then return false, subinfo end iparm = iparm + 2 subunit.inout = 'in' subunit.valinfo = { subinfo } -- Recalculate total as a number of subdivisions. -- subdiv[1] = number of subdivisions per previous unit (integer > 1). total = total * subdiv[1] + subinfo.value if not default then -- set by the first subdiv with a default defined default = subdiv.default end count = count + 1 composite_units[count] = subunit if subdiv.unit or subdiv.name then fixups[count] = { unit = subdiv.unit, name = subdiv.name, valinfo = subunit.valinfo } end end if count == 1 then return true -- no error and no composite unit end for i, fixup in pairs(fixups) do local unit = fixup.unit local name = fixup.name if not unit or (count > 2 and name) then composite_units[i].fixed_name = name else local success, alternate = lookup(parms, unit, 'no_combination') if not success then return false, alternate end -- should never occur alternate.inout = 'in' alternate.valinfo = fixup.valinfo composite_units[i] = alternate end end return true, iparm, { utype = in_unit_table.utype, scale = subunit.scale, -- scale of last (least significant) unit valinfo = { { value = total, clean = subinfo.clean, denominator = subinfo.denominator } }, composite = composite_units, default = default or in_unit_table.default } end local function translate_parms(parms, kv_pairs) -- Update fields in parms by translating each key:value in kv_pairs to terms -- used by this module (may involve translating from local language to English). -- Also, checks are performed which may display warnings, if enabled. -- Return true if successful or return false, t where t is an error message table. currency_text = nil -- local testing can hold module in memory; must clear globals if kv_pairs.adj and kv_pairs.sing then -- For enwiki (before translation), warn if attempt to use adj and sing -- as the latter is a deprecated alias for the former. if kv_pairs.adj ~= kv_pairs.sing and kv_pairs.sing ~= '' then add_warning(parms, 1, 'cvt_unknown_option', 'sing=' .. kv_pairs.sing) end kv_pairs.sing = nil end kv_pairs.comma = kv_pairs.comma or config.comma -- for plwiki who want default comma=5 for loc_name, loc_value in pairs(kv_pairs) do local en_name = text_code.en_option_name[loc_name] if en_name then local en_value = text_code.en_option_value[en_name] if en_value == 'INTEGER' then -- altitude_ft, altitude_m, frac, sigfig en_value = nil if loc_value == '' then add_warning(parms, 2, 'cvt_empty_option', loc_name) else local minimum local number, is_integer = get_number(loc_value) if en_name == 'sigfig' then minimum = 1 elseif en_name == 'frac' then minimum = 2 if number and number < 0 then parms.opt_fraction_horizontal = true number = -number end else minimum = -1e6 end if number and is_integer and number >= minimum then en_value = number else local m if en_name == 'frac' then m = 'cvt_bad_frac' elseif en_name == 'sigfig' then m = 'cvt_bad_sigfig' else m = 'cvt_bad_altitude' end add_warning(parms, 1, m, loc_name .. '=' .. loc_value) end end elseif en_value == 'TEXT' then -- $, input, qid, qual, stylein, styleout, tracking en_value = loc_value ~= '' and loc_value or nil -- accept non-empty user text with no validation if not en_value and (en_name == '$' or en_name == 'qid' or en_name == 'qual') then add_warning(parms, 2, 'cvt_empty_option', loc_name) elseif en_name == '$' then -- Value should be a single character like "€" for the euro currency symbol, but anything is accepted. currency_text = (loc_value == 'euro') and '€' or loc_value elseif en_name == 'input' then -- May have something like {{convert|input=}} (empty input) if source is an infobox -- with optional fields. In that case, want to output nothing rather than an error. parms.input_text = loc_value -- keep input because parms.input is nil if loc_value == '' end else en_value = en_value[loc_value] if en_value and en_value:sub(-1) == '?' then en_value = en_value:sub(1, -2) add_warning(parms, -1, 'cvt_deprecated', loc_name .. '=' .. loc_value) end if en_value == nil then if loc_value == '' then add_warning(parms, 2, 'cvt_empty_option', loc_name) else add_warning(parms, 1, 'cvt_unknown_option', loc_name .. '=' .. loc_value) end elseif en_value == '' then en_value = nil -- an ignored option like adj=off elseif type(en_value) == 'string' and en_value:sub(1, 4) == 'opt_' then for _, v in ipairs(split(en_value, ',')) do local lhs, rhs = v:match('^(.-)=(.+)$') if rhs then parms[lhs] = tonumber(rhs) or rhs else parms[v] = true end end en_value = nil end end parms[en_name] = en_value else add_warning(parms, 1, 'cvt_unknown_option', loc_name .. '=' .. loc_value) end end local abbr_entered = parms.abbr local cfg_abbr = config.abbr if cfg_abbr then -- Don't warn if invalid because every convert would show that warning. if cfg_abbr == 'on always' then parms.abbr = 'on' elseif cfg_abbr == 'off always' then parms.abbr = 'off' elseif parms.abbr == nil then if cfg_abbr == 'on default' then parms.abbr = 'on' elseif cfg_abbr == 'off default' then parms.abbr = 'off' end end end if parms.abbr then if parms.abbr == 'unit' then parms.abbr = 'on' parms.number_word = true end parms.abbr_org = parms.abbr -- original abbr, before any flip elseif parms.opt_hand_hh then parms.abbr_org = 'on' parms.abbr = 'on' else parms.abbr = 'out' -- default is to abbreviate output only (use symbol, not name) end if parms.opt_order_out then -- Disable options that do not work in a useful way with order=out. parms.opt_flip = nil -- override adj=flip parms.opt_spell_in = nil parms.opt_spell_out = nil parms.opt_spell_upper = nil end if parms.opt_spell_out and not abbr_entered then parms.abbr = 'off' -- should show unit name when spelling the output value end if parms.opt_flip then local function swap_in_out(option) local value = parms[option] if value == 'in' then parms[option] = 'out' elseif value == 'out' then parms[option] = 'in' end end swap_in_out('abbr') swap_in_out('lk') if parms.opt_spell_in and not parms.opt_spell_out then -- For simplicity, and because it does not appear to be needed, -- user cannot set an option to spell the output only. parms.opt_spell_in = nil parms.opt_spell_out = true end end if parms.opt_spell_upper then parms.spell_upper = parms.opt_flip and 'out' or 'in' end if parms.opt_table or parms.opt_tablecen then if abbr_entered == nil and parms.lk == nil then parms.opt_values = true end parms.table_align = parms.opt_table and 'right' or 'center' end if parms.table_align or parms.opt_sortable_on then parms.need_table_or_sort = true end local disp_joins = text_code.disp_joins local default_joins = disp_joins['b'] parms.join_between = default_joins[3] or '; ' local disp = parms.disp if disp == nil then -- special case for the most common setting parms.joins = default_joins elseif disp == 'x' then -- Later, parms.joins is set from the input parameters. else -- Old template does this. local abbr = parms.abbr if disp == 'slash' then if abbr_entered == nil then disp = 'slash-nbsp' elseif abbr == 'in' or abbr == 'out' then disp = 'slash-sp' else disp = 'slash-nosp' end elseif disp == 'sqbr' then if abbr == 'on' then disp = 'sqbr-nbsp' else disp = 'sqbr-sp' end end parms.joins = disp_joins[disp] or default_joins parms.join_between = parms.joins[3] or parms.join_between parms.wantname = parms.joins.wantname end if (en_default and not parms.opt_lang_local and (parms[1] or ''):find('%d')) or parms.opt_lang_en then from_en_table = nil end if en_default and from_en_table then -- For hiwiki: localized symbol/name is defined with the US symbol/name field, -- and is used if output uses localized numbers. parms.opt_sp_us = true end return true end local function get_values(parms) -- If successful, update parms and return true, v, i where -- v = table of input values -- i = index to next entry in parms after those processed here -- or return false, t where t is an error message table. local valinfo = collection() -- numbered table of input values local range = collection() -- numbered table of range items (having, for example, 2 range items requires 3 input values) local had_nocomma -- true if removed "nocomma" kludge from second parameter (like "tonocomma") local parm2 = strip(parms[2]) if parm2 and parm2:sub(-7, -1) == 'nocomma' then parms[2] = strip(parm2:sub(1, -8)) parms.opt_nocomma = true had_nocomma = true end local function extractor(i) -- If the parameter is not a value, try unpacking it as a range ("1-23" for "1 to 23"). -- However, "-1-2/3" is a negative fraction (-1⅔), so it must be extracted first. -- Do not unpack a parameter if it is like "3-1/2" which is sometimes incorrectly -- used instead of "3+1/2" (and which should not be interpreted as "3 to ½"). -- Unpacked items are inserted into the parms table. -- The tail recursion allows combinations like "1x2 to 3x4". local valstr = strip(parms[i]) -- trim so any '-' as a negative sign will be at start local success, result = extract_number(parms, valstr, i > 1) if not success and valstr and i < 20 then -- check i to limit abuse local lhs, sep, rhs = valstr:match('^(%S+)%s+(%S+)%s+(%S.*)') if lhs and not (sep == '-' and rhs:match('/')) then if sep:find('%d') then return success, result -- to reject {{convert|1 234 567|m}} with a decent message (en only) end parms[i] = rhs table.insert(parms, i, sep) table.insert(parms, i, lhs) return extractor(i) end if not valstr:match('%-.*/') then for _, sep in ipairs(text_code.ranges.words) do local start, stop = valstr:find(sep, 2, true) -- start at 2 to skip any negative sign for range '-' if start then parms[i] = valstr:sub(stop + 1) table.insert(parms, i, sep) table.insert(parms, i, valstr:sub(1, start - 1)) return extractor(i) end end end end return success, result end local i = 1 local is_change while true do local success, info = extractor(i) -- need to set parms.opt_nocomma before calling this if not success then return false, info end i = i + 1 if is_change then info.is_change = true -- value is after "±" and so is a change (significant for range like {{convert|5|±|5|°C}}) is_change = nil end valinfo:add(info) local range_item = get_range(strip(parms[i])) if not range_item then break end i = i + 1 range:add(range_item) if type(range_item) == 'table' then -- For range "x", if append unit to some values, append it to all. parms.in_range_x = parms.in_range_x or range_item.in_range_x parms.out_range_x = parms.out_range_x or range_item.out_range_x parms.abbr_range_x = parms.abbr_range_x or range_item.abbr_range_x is_change = range_item.is_range_change end end if range.n > 0 then if range.n > 30 then -- limit abuse, although 4 is a more likely upper limit return false, { 'cvt_invalid_num' } -- misleading message but it will do end parms.range = range elseif had_nocomma then return false, { 'cvt_unknown', parm2 } end return true, valinfo, i end local function simple_get_values(parms) -- If input is like "{{convert|valid_value|valid_unit|...}}", -- return true, i, in_unit, in_unit_table -- i = index in parms of what follows valid_unit, if anything. -- The valid_value is not negative and does not use a fraction, and -- no options requiring further processing of the input are used. -- Otherwise, return nothing or return false, parm1 for caller to interpret. -- Testing shows this function is successful for 96% of converts in articles, -- and that on average it speeds up converts by 8%. local clean = to_en(strip(parms[1] or ''), parms) if parms.opt_ri or parms.opt_spell_in or #clean > 10 or not clean:match('^[0-9.]+$') then return false, clean end local value = tonumber(clean) if not value then return end local info = { value = value, altvalue = value, singular = (value == 1), clean = clean, show = with_separator(parms, clean), } local in_unit = strip(parms[2]) local success, in_unit_table = lookup(parms, in_unit, 'no_combination') if not success then return end in_unit_table.valinfo = { info } return true, 3, in_unit, in_unit_table end local function wikidata_call(parms, operation, ...) -- Return true, s where s is the result of a Wikidata operation, -- or return false, t where t is an error message table. local function worker(...) wikidata_code = wikidata_code or require(wikidata_module) wikidata_data = wikidata_data or mw.loadData(wikidata_data_module) return wikidata_code[operation](wikidata_data, ...) end local success, status, result = pcall(worker, ...) if success then return status, result end if parms.opt_sortable_debug then -- Use debug=yes to crash if an error while accessing Wikidata. error('Error accessing Wikidata: ' .. status, 0) end return false, { 'cvt_wd_fail' } end local function get_parms(parms, args) -- If successful, update parms and return true, unit where -- parms is a table of all arguments passed to the template -- converted to named arguments, and -- unit is the input unit table; -- or return false, t where t is an error message table. -- For special processing (not a convert), can also return -- true, wikitext where wikitext is the final result. -- The returned input unit table may be for a fake unit using the specified -- unit code as the symbol and name, and with bad_mcode = message code table. -- MediaWiki removes leading and trailing whitespace from the values of -- named arguments. However, the values of numbered arguments include any -- whitespace entered in the template, and whitespace is used by some -- parameters (example: the numbered parameters associated with "disp=x"). local kv_pairs = {} -- table of input key:value pairs where key is a name; needed because cannot iterate parms and add new fields to it for k, v in pairs(args) do if type(k) == 'number' or k == 'test' then -- parameter "test" is reserved for testing and is not translated parms[k] = v else kv_pairs[k] = v end end if parms.test == 'wikidata' then local ulookup = function (ucode) -- Use empty table for parms so it does not accumulate results when used repeatedly. return lookup({}, ucode, 'no_combination') end return wikidata_call(parms, '_listunits', ulookup) end local success, msg = translate_parms(parms, kv_pairs) if not success then return false, msg end if parms.input then success, msg = wikidata_call(parms, '_adjustparameters', parms, 1) if not success then return false, msg end end local success, i, in_unit, in_unit_table = simple_get_values(parms) if not success then if type(i) == 'string' and i:match('^NNN+$') then -- Some infoboxes have examples like {{convert|NNN|m}} (3 or more "N"). -- Output an empty string for these. return false, { 'cvt_no_output' } end local valinfo success, valinfo, i = get_values(parms) if not success then return false, valinfo end in_unit = strip(parms[i]) i = i + 1 success, in_unit_table = lookup(parms, in_unit, 'no_combination') if not success then in_unit = in_unit or '' if parms.opt_ignore_error then -- display given unit code with no error (for use with {{val}}) in_unit_table = '' -- suppress error message and prevent processing of output unit end in_unit_table = setmetatable({ symbol = in_unit, name2 = in_unit, utype = in_unit, scale = 1, default = '', defkey = '', linkey = '', bad_mcode = in_unit_table }, unit_mt) end in_unit_table.valinfo = valinfo end if parms.test == 'msg' then -- Am testing the messages produced when no output unit is specified, and -- the input unit has a missing or invalid default. -- Set two units for testing that. -- LATER: Remove this code. if in_unit == 'chain' then in_unit_table.default = nil -- no default elseif in_unit == 'rd' then in_unit_table.default = "ft!X!m" -- an invalid expression end end in_unit_table.inout = 'in' -- this is an input unit if not parms.range then local success, inext, composite_unit = get_composite(parms, i, in_unit_table) if not success then return false, inext end if composite_unit then in_unit_table = composite_unit i = inext end end if in_unit_table.builtin == 'mach' then -- As with old template, a number following Mach as the input unit is the altitude. -- That is deprecated: should use altitude_ft=NUMBER or altitude_m=NUMBER. local success, info success = tonumber(parms[i]) -- this will often work and will give correct result for values like 2e4 without forcing output scientific notation if success then info = { value = success } else success, info = extract_number(parms, parms[i], false, true) end if success then i = i + 1 in_unit_table.altitude = info.value end end local word = strip(parms[i]) i = i + 1 local precision, is_bad_precision local function set_precision(text) local number, is_integer = get_number(text) if number then if is_integer then precision = number else precision = text is_bad_precision = true end return true -- text was used for precision, good or bad end end if word and not set_precision(word) then parms.out_unit = parms.out_unit or word if set_precision(strip(parms[i])) then i = i + 1 end end if parms.opt_adj_mid then word = parms[i] i = i + 1 if word then -- mid-text words if word:sub(1, 1) == '-' then parms.mid = word else parms.mid = ' ' .. word end end end if parms.opt_one_preunit then parms[parms.opt_flip and 'preunit2' or 'preunit1'] = preunits(1, parms[i]) i = i + 1 end if parms.disp == 'x' then -- Following is reasonably compatible with the old template. local first = parms[i] or '' local second = parms[i+1] or '' i = i + 2 if strip(first) == '' then -- user can enter '&#32;' rather than ' ' to avoid the default first = ' [&nbsp;' .. first second = '&nbsp;]' .. second end parms.joins = { first, second } elseif parms.opt_two_preunits then local p1, p2 = preunits(2, parms[i], parms[i+1]) i = i + 2 if parms.preunit1 then -- To simplify documentation, allow unlikely use of adj=pre with disp=preunit -- (however, an output unit must be specified with adj=pre and with disp=preunit). parms.preunit1 = parms.preunit1 .. p1 parms.preunit2 = p2 else parms.preunit1, parms.preunit2 = p1, p2 end end if precision == nil then if set_precision(strip(parms[i])) then i = i + 1 end end if is_bad_precision then add_warning(parms, 1, 'cvt_bad_prec', precision) else parms.precision = precision end for j = i, i + 3 do local parm = parms[j] -- warn if find a non-empty extraneous parameter if parm and parm:match('%S') then add_warning(parms, 1, 'cvt_unknown_option', parm) break end end return true, in_unit_table end local function record_default_precision(parms, out_current, precision) -- If necessary, adjust parameters and return a possibly adjusted precision. -- When converting a range of values where a default precision is required, -- that default is calculated for each value because the result sometimes -- depends on the precise input and output values. This function may cause -- the entire convert process to be repeated in order to ensure that the -- same default precision is used for each individual convert. -- If that were not done, a range like 1000 to 1000.4 may give poor results -- because the first output could be heavily rounded, while the second is not. -- For range 1000.4 to 1000, this function can give the second convert the -- same default precision that was used for the first. if not parms.opt_round_each then local maxdef = out_current.max_default_precision if maxdef then if maxdef < precision then parms.do_convert_again = true out_current.max_default_precision = precision else precision = out_current.max_default_precision end else out_current.max_default_precision = precision end end return precision end local function default_precision(parms, invalue, inclean, denominator, outvalue, in_current, out_current, extra) -- Return a default value for precision (an integer like 2, 0, -2). -- If denominator is not nil, it is the value of the denominator in inclean. -- Code follows procedures used in old template. local fudge = 1e-14 -- {{Order of magnitude}} adds this, so we do too local prec, minprec, adjust local subunit_ignore_trailing_zero local subunit_more_precision -- kludge for "in" used in input like "|2|ft|6|in" local composite = in_current.composite if composite then subunit_ignore_trailing_zero = true -- input "|2|st|10|lb" has precision 0, not -1 if composite[#composite].exception == 'subunit_more_precision' then subunit_more_precision = true -- do not use standard precision with input like "|2|ft|6|in" end end if denominator and denominator > 0 then prec = math.max(log10(denominator), 1) else -- Count digits after decimal mark, handling cases like '12.345e6'. local exponent local integer, dot, decimals, expstr = inclean:match('^(%d*)(%.?)(%d*)(.*)') local e = expstr:sub(1, 1) if e == 'e' or e == 'E' then exponent = tonumber(expstr:sub(2)) end if dot == '' then prec = subunit_ignore_trailing_zero and 0 or -integer:match('0*$'):len() else prec = #decimals end if exponent then -- So '1230' and '1.23e3' both give prec = -1, and '0.00123' and '1.23e-3' give 5. prec = prec - exponent end end if in_current.istemperature and out_current.istemperature then -- Converting between common temperatures (°C, °F, °R, K); not keVT. -- Kelvin value can be almost zero, or small but negative due to precision problems. -- Also, an input value like -300 C (below absolute zero) gives negative kelvins. -- Calculate minimum precision from absolute value. adjust = 0 local kelvin = abs((invalue - in_current.offset) * in_current.scale) if kelvin < 1e-8 then -- assume nonzero due to input or calculation precision problem minprec = 2 else minprec = 2 - floor(log10(kelvin) + fudge) -- 3 sigfigs in kelvin end else if invalue == 0 or outvalue <= 0 then -- We are never called with a negative outvalue, but it might be zero. -- This is special-cased to avoid calculation exceptions. return record_default_precision(parms, out_current, 0) end if out_current.exception == 'integer_more_precision' and floor(invalue) == invalue then -- With certain output units that sometimes give poor results -- with default rounding, use more precision when the input -- value is equal to an integer. An example of a poor result -- is when input 50 gives a smaller output than input 49.5. -- Experiment shows this helps, but it does not eliminate all -- surprises because it is not clear whether "50" should be -- interpreted as "from 45 to 55" or "from 49.5 to 50.5". adjust = -log10(in_current.scale) elseif subunit_more_precision then -- Conversion like "{{convert|6|ft|1|in|cm}}" (where subunit is "in") -- has a non-standard adjust value, to give more output precision. adjust = log10(out_current.scale) + 2 else adjust = log10(abs(invalue / outvalue)) end adjust = adjust + log10(2) -- Ensure that the output has at least two significant figures. minprec = 1 - floor(log10(outvalue) + fudge) end if extra then adjust = extra.adjust or adjust minprec = extra.minprec or minprec end return record_default_precision(parms, out_current, math.max(floor(prec + adjust), minprec)) end local function convert(parms, invalue, info, in_current, out_current) -- Convert given input value from one unit to another. -- Return output_value (a number) if a simple convert, or -- return f, t where -- f = true, t = table of information with results, or -- f = false, t = error message table. local inscale = in_current.scale local outscale = out_current.scale if not in_current.iscomplex and not out_current.iscomplex then return invalue * (inscale / outscale) -- minimize overhead for most common case end if in_current.invert or out_current.invert then -- Inverted units, such as inverse length, inverse time, or -- fuel efficiency. Built-in units do not have invert set. if (in_current.invert or 1) * (out_current.invert or 1) < 0 then return 1 / (invalue * inscale * outscale) end return invalue * (inscale / outscale) elseif in_current.offset then -- Temperature (there are no built-ins for this type of unit). if info.is_change then return invalue * (inscale / outscale) end return (invalue - in_current.offset) * (inscale / outscale) + out_current.offset else -- Built-in unit. local in_builtin = in_current.builtin local out_builtin = out_current.builtin if in_builtin and out_builtin then if in_builtin == out_builtin then return invalue end -- There are no cases (yet) where need to convert from one -- built-in unit to another, so this should never occur. return false, { 'cvt_bug_convert' } end if in_builtin == 'mach' or out_builtin == 'mach' then -- Should check that only one altitude is given but am planning to remove -- in_current.altitude (which can only occur when Mach is the input unit), -- and out_current.altitude cannot occur. local alt = parms.altitude_ft or in_current.altitude if not alt and parms.altitude_m then alt = parms.altitude_m / 0.3048 -- 1 ft = 0.3048 m end local spd = speed_of_sound(alt) if in_builtin == 'mach' then inscale = spd return invalue * (inscale / outscale) end outscale = spd local adjust = 0.1 / inscale return true, { outvalue = invalue * (inscale / outscale), adjust = log10(adjust) + log10(2), } elseif in_builtin == 'hand' then -- 1 hand = 4 inches; 1.2 hands = 6 inches. -- Decimals of a hand are only defined for the first digit, and -- the first fractional digit should be a number of inches (1, 2 or 3). -- However, this code interprets the entire fractional part as the number -- of inches / 10 (so 1.75 inches would be 0.175 hands). -- A value like 12.3 hands is exactly 12*4 + 3 inches; base default precision on that. local integer, fracpart = math.modf(invalue) local inch_value = 4 * integer + 10 * fracpart -- equivalent number of inches local factor = inscale / outscale if factor == 4 then -- Am converting to inches: show exact result, and use "inches" not "in" by default. if parms.abbr_org == nil then out_current.usename = true end local show = format('%g', abs(inch_value)) -- show and clean are unsigned if not show:find('e', 1, true) then return true, { invalue = inch_value, outvalue = inch_value, clean = show, show = show, } end end local outvalue = (integer + 2.5 * fracpart) * factor local fracstr = info.clean:match('%.(.*)') or '' local fmt if fracstr == '' then fmt = '%.0f' else fmt = '%.' .. format('%d', #fracstr - 1) .. 'f' end return true, { invalue = inch_value, clean = format(fmt, inch_value), outvalue = outvalue, minprec = 0, } end end return false, { 'cvt_bug_convert' } -- should never occur end local function user_style(parms, i) -- Return text for a user-specified style for a table cell, or '' if none, -- given i = 1 (input style) or 2 (output style). local style = parms[(i == 1) and 'stylein' or 'styleout'] if style then style = style:gsub('"', '') if style ~= '' then if style:sub(-1) ~= ';' then style = style .. ';' end return style end end return '' end local function make_table_or_sort(parms, invalue, info, in_current, scaled_top) -- Set options to handle output for a table or a sort key, or both. -- The text sort key is based on the value resulting from converting -- the input to a fake base unit with scale = 1, and other properties -- required for a conversion derived from the input unit. -- For other modules, return the sort key in a hidden span element, and -- the scaled value used to generate the sort key. -- If scaled_top is set, it is the scaled value of the numerator of a per unit -- to be combined with this unit (the denominator) to make the sort key. -- Scaling only works with units that convert with a factor (not temperature). local sortkey, scaled_value if parms.opt_sortable_on then local base = { -- a fake unit with enough fields for a valid convert scale = 1, invert = in_current.invert and 1, iscomplex = in_current.iscomplex, offset = in_current.offset and 0, } local outvalue, extra = convert(parms, invalue, info, in_current, base) if extra then outvalue = extra.outvalue end if in_current.istemperature then -- Have converted to kelvin; assume numbers close to zero have a -- rounding error and should be zero. if abs(outvalue) < 1e-12 then outvalue = 0 end end if scaled_top and outvalue ~= 0 then outvalue = scaled_top / outvalue end scaled_value = outvalue if not valid_number(outvalue) then if outvalue < 0 then sortkey = '1000000000000000000' else sortkey = '9000000000000000000' end elseif outvalue == 0 then sortkey = '5000000000000000000' else local mag = floor(log10(abs(outvalue)) + 1e-14) local prefix if outvalue > 0 then prefix = 7000 + mag else prefix = 2999 - mag outvalue = outvalue + 10^(mag+1) end sortkey = format('%d', prefix) .. format('%015.0f', floor(outvalue * 10^(14-mag))) end end local sortspan if sortkey and not parms.table_align then sortspan = parms.opt_sortable_debug and '<span data-sort-value="' .. sortkey .. '♠"><span style="border:1px solid">' .. sortkey .. '♠</span></span>' or '<span data-sort-value="' .. sortkey .. '♠"></span>' parms.join_before = sortspan end if parms.table_align then local sort if sortkey then sort = ' data-sort-value="' .. sortkey .. '"' if parms.opt_sortable_debug then parms.join_before = '<span style="border:1px solid">' .. sortkey .. '</span>' end else sort = '' end local style = 'style="text-align:' .. parms.table_align .. ';' local joins = {} for i = 1, 2 do joins[i] = (i == 1 and '' or '\n|') .. style .. user_style(parms, i) .. '"' .. sort .. '|' end parms.table_joins = joins end return sortspan, scaled_value end local cvt_to_hand local function cvtround(parms, info, in_current, out_current) -- Return true, t where t is a table with the conversion results; fields: -- show = rounded, formatted string with the result of converting value in info, -- using the rounding specified in parms. -- singular = true if result (after rounding and ignoring any negative sign) -- is "1", or like "1.00", or is a fraction with value < 1; -- (and more fields shown below, and a calculated 'absvalue' field). -- or return false, t where t is an error message table. -- Input info.clean uses en digits (it has been translated, if necessary). -- Output show uses en or non-en digits as appropriate, or can be spelled. if out_current.builtin == 'hand' then return cvt_to_hand(parms, info, in_current, out_current) end local invalue = in_current.builtin == 'hand' and info.altvalue or info.value local outvalue, extra = convert(parms, invalue, info, in_current, out_current) if parms.need_table_or_sort then parms.need_table_or_sort = nil -- process using first input value only make_table_or_sort(parms, invalue, info, in_current) end if extra then if not outvalue then return false, extra end invalue = extra.invalue or invalue outvalue = extra.outvalue end if not valid_number(outvalue) then return false, { 'cvt_invalid_num' } end local isnegative if outvalue < 0 then isnegative = true outvalue = -outvalue end local precision, show, exponent local denominator = out_current.frac if denominator then show = fraction_table(outvalue, denominator) else precision = parms.precision if not precision then if parms.sigfig then show, exponent = make_sigfig(outvalue, parms.sigfig) elseif parms.opt_round then local n = parms.opt_round if n == 0.5 then local integer, fracpart = math.modf(floor(2 * outvalue + 0.5) / 2) if fracpart == 0 then show = format('%.0f', integer) else show = format('%.1f', integer + fracpart) end else show = format('%.0f', floor((outvalue / n) + 0.5) * n) end elseif in_current.builtin == 'mach' then local sigfig = info.clean:gsub('^[0.]+', ''):gsub('%.', ''):len() + 1 show, exponent = make_sigfig(outvalue, sigfig) else local inclean = info.clean if extra then inclean = extra.clean or inclean show = extra.show end if not show then precision = default_precision(parms, invalue, inclean, info.denominator, outvalue, in_current, out_current, extra) end end end end if precision then if precision >= 0 then local fudge if precision <= 8 then -- Add a fudge to handle common cases of bad rounding due to inability -- to precisely represent some values. This makes the following work: -- {{convert|-100.1|C|K}} and {{convert|5555000|um|m|2}}. -- Old template uses #expr round, which invokes PHP round(). -- LATER: Investigate how PHP round() works. fudge = 2e-14 else fudge = 0 end local fmt = '%.' .. format('%d', precision) .. 'f' local success success, show = pcall(format, fmt, outvalue + fudge) if not success then return false, { 'cvt_big_prec', tostring(precision) } end else precision = -precision -- #digits to zero (in addition to any digits after dot) local shift = 10 ^ precision show = format('%.0f', outvalue/shift) if show ~= '0' then exponent = #show + precision end end end local t = format_number(parms, show, exponent, isnegative) if type(show) == 'string' then -- Set singular using match because on some systems 0.99999999999999999 is 1.0. if exponent then t.singular = (exponent == 1 and show:match('^10*$')) else t.singular = (show == '1' or show:match('^1%.0*$')) end else t.fraction_table = show t.singular = (outvalue <= 1) -- cannot have 'fraction == 1', but if it were possible it would be singular end t.raw_absvalue = outvalue -- absolute value before rounding return true, setmetatable(t, { __index = function (self, key) if key == 'absvalue' then -- Calculate absolute value after rounding, if needed. local clean, exponent = rawget(self, 'clean'), rawget(self, 'exponent') local value = tonumber(clean) -- absolute value (any negative sign has been ignored) if exponent then value = value * 10^exponent end rawset(self, key, value) return value end end }) end function cvt_to_hand(parms, info, in_current, out_current) -- Convert input to hands, inches. -- Return true, t where t is a table with the conversion results; -- or return false, t where t is an error message table. if parms.abbr_org == nil then out_current.usename = true -- default is to show name not symbol end local precision = parms.precision local frac = out_current.frac if not frac and precision and precision > 1 then frac = (precision == 2) and 2 or 4 end local out_next = out_current.out_next if out_next then -- Use magic knowledge to determine whether the next unit is inches without requiring i18n. -- The following ensures that when the output combination "hand in" is used, the inches -- value is rounded to match the hands value. Also, displaying say "61½" instead of 61.5 -- is better as 61.5 implies the value is not 61.4. if out_next.exception == 'subunit_more_precision' then out_next.frac = frac end end -- Convert to inches; calculate hands from that. local dummy_unit_table = { scale = out_current.scale / 4, frac = frac } local success, outinfo = cvtround(parms, info, in_current, dummy_unit_table) if not success then return false, outinfo end local tfrac = outinfo.fraction_table local inches = outinfo.raw_absvalue if tfrac then inches = floor(inches) -- integer part only; fraction added later else inches = floor(inches + 0.5) -- a hands measurement never shows decimals of an inch end local hands, inches = divide(inches, 4) outinfo.absvalue = hands + inches/4 -- supposed to be the absolute rounded value, but this is close enough local inchstr = tostring(inches) -- '0', '1', '2' or '3' if precision and precision <= 0 then -- using negative or 0 for precision rounds to nearest hand hands = floor(outinfo.raw_absvalue/4 + 0.5) inchstr = '' elseif tfrac then -- Always show an integer before fraction (like "15.0½") because "15½" means 15-and-a-half hands. inchstr = numdot .. format_fraction(parms, 'out', false, inchstr, tfrac.numstr, tfrac.denstr) else inchstr = numdot .. from_en(inchstr) end outinfo.show = outinfo.sign .. with_separator(parms, format('%.0f', hands)) .. inchstr return true, outinfo end local function evaluate_condition(value, condition) -- Return true or false from applying a conditional expression to value, -- or throw an error if invalid. -- A very limited set of expressions is supported: -- v < 9 -- v * 9 < 9 -- where -- 'v' is replaced with value -- 9 is any number (as defined by Lua tonumber) -- only en digits are accepted -- '<' can also be '<=' or '>' or '>=' -- In addition, the following form is supported: -- LHS and RHS -- where -- LHS, RHS = any of above expressions. local function compare(value, text) local arithop, factor, compop, limit = text:match('^%s*v%s*([*]?)(.-)([<>]=?)(.*)$') if arithop == nil then error('Invalid default expression', 0) elseif arithop == '*' then factor = tonumber(factor) if factor == nil then error('Invalid default expression', 0) end value = value * factor end limit = tonumber(limit) if limit == nil then error('Invalid default expression', 0) end if compop == '<' then return value < limit elseif compop == '<=' then return value <= limit elseif compop == '>' then return value > limit elseif compop == '>=' then return value >= limit end error('Invalid default expression', 0) -- should not occur end local lhs, rhs = condition:match('^(.-%W)and(%W.*)') if lhs == nil then return compare(value, condition) end return compare(value, lhs) and compare(value, rhs) end local function get_default(value, unit_table) -- Return true, s where s = name of unit's default output unit, -- or return false, t where t is an error message table. -- Some units have a default that depends on the input value -- (the first value if a range of values is used). -- If '!' is in the default, the first bang-delimited field is an -- expression that uses 'v' to represent the input value. -- Example: 'v < 120 ! small ! big ! suffix' (suffix is optional) -- evaluates 'v < 120' as a boolean with result -- 'smallsuffix' if (value < 120), or 'bigsuffix' otherwise. -- Input must use en digits and '.' decimal mark. local default = data_code.default_exceptions[unit_table.defkey or unit_table.symbol] or unit_table.default if not default then local per = unit_table.per if per then local function a_default(v, u) local success, ucode = get_default(v, u) if not success then return '?' -- an unlikely error has occurred; will cause lookup of default to fail end -- Attempt to use only the first unit if a combination or output multiple. -- This is not bulletproof but should work for most cases. -- Where it does not work, the convert will need to specify the wanted output unit. local t = all_units[ucode] if t then local combo = t.combination if combo then -- For a multiple like ftin, the "first" unit (ft) is last in the combination. local i = t.multiple and table_len(combo) or 1 ucode = combo[i] end else -- Try for an automatically generated combination. local item = ucode:match('^(.-)%+') or ucode:match('^(%S+)%s') if all_units[item] then return item end end return ucode end local unit1, unit2 = per[1], per[2] local def1 = (unit1 and a_default(value, unit1) or unit_table.vprefix or '') local def2 = a_default(1, unit2) -- 1 because per unit of denominator return true, def1 .. '/' .. def2 end return false, { 'cvt_no_default', unit_table.symbol } end if default:find('!', 1, true) == nil then return true, default end local t = split(default, '!') if #t == 3 or #t == 4 then local success, result = pcall(evaluate_condition, value, t[1]) if success then default = result and t[2] or t[3] if #t == 4 then default = default .. t[4] end return true, default end end return false, { 'cvt_bad_default', unit_table.symbol } end local linked_pages -- to record linked pages so will not link to the same page more than once local function unlink(unit_table) -- Forget that the given unit has previously been linked (if it has). -- That is needed when processing a range of inputs or outputs when an id -- for the first range value may have been evaluated, but only an id for -- the last value is displayed, and that id may need to be linked. linked_pages[unit_table.unitcode or unit_table] = nil end local function make_link(link, id, unit_table) -- Return wikilink "[[link|id]]", possibly abbreviated as in examples: -- [[Mile|mile]] --> [[mile]] -- [[Mile|miles]] --> [[mile]]s -- However, just id is returned if: -- * no link given (so caller does not need to check if a link was defined); or -- * link has previously been used during the current convert (to avoid overlinking). local link_key if unit_table then link_key = unit_table.unitcode or unit_table else link_key = link end if not link or link == '' or linked_pages[link_key] then return id end linked_pages[link_key] = true -- Following only works for language en, but it should be safe on other wikis, -- and overhead of doing it generally does not seem worthwhile. local l = link:sub(1, 1):lower() .. link:sub(2) if link == id or l == id then return '[[' .. id .. ']]' elseif link .. 's' == id or l .. 's' == id then return '[[' .. id:sub(1, -2) .. ']]s' else return '[[' .. link .. '|' .. id .. ']]' end end local function variable_name(clean, unit_table) -- For slwiki, a unit name depends on the value. -- Parameter clean is the unsigned rounded value in en digits, as a string. -- Value Source Example for "m" -- integer 1: name1 meter (also is the name of the unit) -- integer 2: var{1} metra -- integer 3 and 4: var{2} metri -- integer else: var{3} metrov (0 and 5 or more) -- real/fraction: var{4} metra -- var{i} means the i'th field in unit_table.varname if it exists and has -- an i'th field, otherwise name2. -- Fields are separated with "!" and are not empty. -- A field for a unit using an SI prefix has the prefix name inserted, -- replacing '#' if found, or before the field otherwise. local vname if clean == '1' then vname = unit_table.name1 elseif unit_table.varname then local i if clean == '2' then i = 1 elseif clean == '3' or clean == '4' then i = 2 elseif clean:find('.', 1, true) then i = 4 else i = 3 end if i > 1 and varname == 'pl' then i = i - 1 end vname = split(unit_table.varname, '!')[i] end if vname then local si_name = rawget(unit_table, 'si_name') or '' local pos = vname:find('#', 1, true) if pos then vname = vname:sub(1, pos - 1) .. si_name .. vname:sub(pos + 1) else vname = si_name .. vname end return vname end return unit_table.name2 end local function linked_id(parms, unit_table, key_id, want_link, clean) -- Return final unit id (symbol or name), optionally with a wikilink, -- and update unit_table.sep if required. -- key_id is one of: 'symbol', 'sym_us', 'name1', 'name1_us', 'name2', 'name2_us'. local abbr_on = (key_id == 'symbol' or key_id == 'sym_us') if abbr_on and want_link then local symlink = rawget(unit_table, 'symlink') if symlink then return symlink -- for exceptions that have the linked symbol built-in end end local multiplier = rawget(unit_table, 'multiplier') local per = unit_table.per if per then local paren1, paren2 = '', '' -- possible parentheses around bottom unit local unit1 = per[1] -- top unit_table, or nil local unit2 = per[2] -- bottom unit_table if abbr_on then if not unit1 then unit_table.sep = '' -- no separator in "$2/acre" end if not want_link then local symbol = unit_table.symbol_raw if symbol then return symbol -- for exceptions that have the symbol built-in end end if (unit2.symbol):find('⋅', 1, true) then paren1, paren2 = '(', ')' end end local key_id2 -- unit2 is always singular if key_id == 'name2' then key_id2 = 'name1' elseif key_id == 'name2_us' then key_id2 = 'name1_us' else key_id2 = key_id end local result if abbr_on then result = '/' elseif omitsep then result = per_word elseif unit1 then result = ' ' .. per_word .. ' ' else result = per_word .. ' ' end if want_link and unit_table.link then if abbr_on or not varname then result = (unit1 and linked_id(parms, unit1, key_id, false, clean) or '') .. result .. linked_id(parms, unit2, key_id2, false, '1') else result = (unit1 and variable_name(clean, unit1) or '') .. result .. variable_name('1', unit2) end if omit_separator(result) then unit_table.sep = '' end return make_link(unit_table.link, result, unit_table) end if unit1 then result = linked_id(parms, unit1, key_id, want_link, clean) .. result if unit1.sep then unit_table.sep = unit1.sep end elseif omitsep then unit_table.sep = '' end return result .. paren1 .. linked_id(parms, unit2, key_id2, want_link, '1') .. paren2 end if multiplier then -- A multiplier (like "100" in "100km") forces the unit to be plural. multiplier = from_en(multiplier) if not omitsep then multiplier = multiplier .. (abbr_on and '&nbsp;' or ' ') end if not abbr_on then if key_id == 'name1' then key_id = 'name2' elseif key_id == 'name1_us' then key_id = 'name2_us' end end else multiplier = '' end local id = unit_table.fixed_name or ((varname and not abbr_on) and variable_name(clean, unit_table) or unit_table[key_id]) if omit_separator(id) then unit_table.sep = '' end if want_link then local link = data_code.link_exceptions[unit_table.linkey or unit_table.symbol] or unit_table.link if link then local before = '' local i = unit_table.customary if i == 1 and parms.opt_sp_us then i = 2 -- show "U.S." not "US" end if i == 3 and abbr_on then i = 4 -- abbreviate "imperial" to "imp" end local customary = text_code.customary_units[i] if customary then -- LATER: This works for language en only, but it's esoteric so ignore for now. local pertext if id:sub(1, 1) == '/' then -- Want unit "/USgal" to display as "/U.S. gal", not "U.S. /gal". pertext = '/' id = id:sub(2) elseif id:sub(1, 4) == 'per ' then -- Similarly want "per U.S. gallon", not "U.S. per gallon" (but in practice this is unlikely to be used). pertext = 'per ' id = id:sub(5) else pertext = '' end -- Omit any "US"/"U.S."/"imp"/"imperial" from start of id since that will be inserted. local removes = (i < 3) and { 'US&nbsp;', 'US ', 'U.S.&nbsp;', 'U.S. ' } or { 'imp&nbsp;', 'imp ', 'imperial ' } for _, prefix in ipairs(removes) do local plen = #prefix if id:sub(1, plen) == prefix then id = id:sub(plen + 1) break end end before = pertext .. make_link(customary.link, customary[1]) .. ' ' end id = before .. make_link(link, id, unit_table) end end return multiplier .. id end local function make_id(parms, which, unit_table) -- Return id, f where -- id = unit name or symbol, possibly modified -- f = true if id is a name, or false if id is a symbol -- using the value for index 'which', and for 'in' or 'out' (unit_table.inout). -- Result is '' if no symbol/name is to be used. -- In addition, set unit_table.sep = ' ' or '&nbsp;' or '' -- (the separator that caller will normally insert before the id). if parms.opt_values then unit_table.sep = '' return '' end local inout = unit_table.inout local info = unit_table.valinfo[which] local abbr_org = parms.abbr_org local adjectival = parms.opt_adjectival local lk = parms.lk local want_link = (lk == 'on' or lk == inout) local usename = unit_table.usename local singular = info.singular local want_name if usename then want_name = true else if abbr_org == nil then if parms.wantname then want_name = true end if unit_table.usesymbol then want_name = false end end if want_name == nil then local abbr = parms.abbr if abbr == 'on' or abbr == inout or (abbr == 'mos' and inout == 'out') then want_name = false else want_name = true end end end local key if want_name then if lk == nil and unit_table.builtin == 'hand' then want_link = true end if parms.opt_use_nbsp then unit_table.sep = '&nbsp;' else unit_table.sep = ' ' end if parms.opt_singular then local value if inout == 'in' then value = info.value else value = info.absvalue end if value then -- some unusual units do not always set value field value = abs(value) singular = (0 < value and value < 1.0001) end end if unit_table.engscale then -- engscale: so "|1|e3kg" gives "1 thousand kilograms" (plural) singular = false end key = (adjectival or singular) and 'name1' or 'name2' if parms.opt_sp_us then key = key .. '_us' end else if unit_table.builtin == 'hand' then if parms.opt_hand_hh then unit_table.symbol = 'hh' -- LATER: might want i18n applied to this end end unit_table.sep = '&nbsp;' key = parms.opt_sp_us and 'sym_us' or 'symbol' end return linked_id(parms, unit_table, key, want_link, info.clean), want_name end local function decorate_value(parms, unit_table, which, number_word) -- If needed, update unit_table so values will be shown with extra information. -- For consistency with the old template (but different from fmtpower), -- the style to display powers of 10 includes "display:none" to allow some -- browsers to copy, for example, "10³" as "10^3", rather than as "103". local info local engscale = unit_table.engscale local prefix = unit_table.vprefix if engscale or prefix then info = unit_table.valinfo[which] if info.decorated then return -- do not redecorate if repeating convert end info.decorated = true if engscale then local inout = unit_table.inout local abbr = parms.abbr if (abbr == 'on' or abbr == inout) and not parms.number_word then info.show = info.show .. '<span style="margin-left:0.2em">×<span style="margin-left:0.1em">' .. from_en('10') .. '</span></span><s style="display:none">^</s><sup>' .. from_en(tostring(engscale.exponent)) .. '</sup>' elseif number_word then local number_id local lk = parms.lk if lk == 'on' or lk == inout then number_id = make_link(engscale.link, engscale[1]) else number_id = engscale[1] end -- WP:NUMERAL recommends "&nbsp;" in values like "12 million". info.show = info.show .. (parms.opt_adjectival and '-' or '&nbsp;') .. number_id end end if prefix then info.show = prefix .. info.show end end end local function process_input(parms, in_current) -- Processing required once per conversion. -- Return block of text to represent input (value/unit). if parms.opt_output_only or parms.opt_output_number_only or parms.opt_output_unit_only then parms.joins = { '', '' } return '' end local first_unit local composite = in_current.composite -- nil or table of units if composite then first_unit = composite[1] else first_unit = in_current end local id1, want_name = make_id(parms, 1, first_unit) local sep = first_unit.sep -- separator between value and unit, set by make_id local preunit = parms.preunit1 if preunit then sep = '' -- any separator is included in preunit else preunit = '' end if parms.opt_input_unit_only then parms.joins = { '', '' } if composite then local parts = { id1 } for i, unit in ipairs(composite) do if i > 1 then table.insert(parts, (make_id(parms, 1, unit))) end end id1 = table.concat(parts, ' ') end if want_name and parms.opt_adjectival then return preunit .. hyphenated(id1) end return preunit .. id1 end if parms.opt_also_symbol and not composite and not parms.opt_flip then local join1 = parms.joins[1] if join1 == ' (' or join1 == ' [' then parms.joins = { ' [' .. first_unit[parms.opt_sp_us and 'sym_us' or 'symbol'] .. ']' .. join1 , parms.joins[2] } end end if in_current.builtin == 'mach' and first_unit.sep ~= '' then -- '' means omitsep with non-enwiki name local prefix = id1 .. '&nbsp;' local range = parms.range local valinfo = first_unit.valinfo local result = prefix .. valinfo[1].show if range then -- For simplicity and because more not needed, handle one range item only. local prefix2 = make_id(parms, 2, first_unit) .. '&nbsp;' result = range_text(range[1], want_name, parms, result, prefix2 .. valinfo[2].show, 'in', {spaced=true}) end return preunit .. result end if composite then -- Simplify: assume there is no range, and no decoration. local mid = (not parms.opt_flip) and parms.mid or '' local sep1 = '&nbsp;' local sep2 = ' ' if parms.opt_adjectival and want_name then sep1 = '-' sep2 = '-' end if omitsep and sep == '' then -- Testing the id of the most significant unit should be sufficient. sep1 = '' sep2 = '' end local parts = { first_unit.valinfo[1].show .. sep1 .. id1 } for i, unit in ipairs(composite) do if i > 1 then table.insert(parts, unit.valinfo[1].show .. sep1 .. (make_id(parms, 1, unit))) end end return table.concat(parts, sep2) .. mid end local add_unit = (parms.abbr == 'mos') or parms[parms.opt_flip and 'out_range_x' or 'in_range_x'] or (not want_name and parms.abbr_range_x) local range = parms.range if range and not add_unit then unlink(first_unit) end local id = range and make_id(parms, range.n + 1, first_unit) or id1 local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, 'in') if was_hyphenated then add_unit = false end local result local valinfo = first_unit.valinfo if range then for i = 0, range.n do local number_word if i == range.n then add_unit = false number_word = true end decorate_value(parms, first_unit, i+1, number_word) local show = valinfo[i+1].show if add_unit then show = show .. first_unit.sep .. (i == 0 and id1 or make_id(parms, i+1, first_unit)) end if i == 0 then result = show else result = range_text(range[i], want_name, parms, result, show, 'in') end end else decorate_value(parms, first_unit, 1, true) result = valinfo[1].show end return result .. preunit .. extra end local function process_one_output(parms, out_current) -- Processing required for each output unit. -- Return block of text to represent output (value/unit). local inout = out_current.inout -- normally 'out' but can be 'in' for order=out local id1, want_name = make_id(parms, 1, out_current) local sep = out_current.sep -- set by make_id local preunit = parms.preunit2 if preunit then sep = '' -- any separator is included in preunit else preunit = '' end if parms.opt_output_unit_only then if want_name and parms.opt_adjectival then return preunit .. hyphenated(id1) end return preunit .. id1 end if out_current.builtin == 'mach' and out_current.sep ~= '' then -- '' means omitsep with non-enwiki name local prefix = id1 .. '&nbsp;' local range = parms.range local valinfo = out_current.valinfo local result = prefix .. valinfo[1].show if range then -- For simplicity and because more not needed, handle one range item only. result = range_text(range[1], want_name, parms, result, prefix .. valinfo[2].show, inout, {spaced=true}) end return preunit .. result end local add_unit = (parms[parms.opt_flip and 'in_range_x' or 'out_range_x'] or (not want_name and parms.abbr_range_x)) and not parms.opt_output_number_only local range = parms.range if range and not add_unit then unlink(out_current) end local id = range and make_id(parms, range.n + 1, out_current) or id1 local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, inout) if was_hyphenated then add_unit = false end local result local valinfo = out_current.valinfo if range then for i = 0, range.n do local number_word if i == range.n then add_unit = false number_word = true end decorate_value(parms, out_current, i+1, number_word) local show = valinfo[i+1].show if add_unit then show = show .. out_current.sep .. (i == 0 and id1 or make_id(parms, i+1, out_current)) end if i == 0 then result = show else result = range_text(range[i], want_name, parms, result, show, inout) end end else decorate_value(parms, out_current, 1, true) result = valinfo[1].show end if parms.opt_output_number_only then return result end return result .. preunit .. extra end local function make_output_single(parms, in_unit_table, out_unit_table) -- Return true, item where item = wikitext of the conversion result -- for a single output (which is not a combination or a multiple); -- or return false, t where t is an error message table. if parms.opt_order_out and in_unit_table.unitcode == out_unit_table.unitcode then out_unit_table.valinfo = in_unit_table.valinfo else out_unit_table.valinfo = collection() for _, v in ipairs(in_unit_table.valinfo) do local success, info = cvtround(parms, v, in_unit_table, out_unit_table) if not success then return false, info end out_unit_table.valinfo:add(info) end end return true, process_one_output(parms, out_unit_table) end local function make_output_multiple(parms, in_unit_table, out_unit_table) -- Return true, item where item = wikitext of the conversion result -- for an output which is a multiple (like 'ftin'); -- or return false, t where t is an error message table. local inout = out_unit_table.inout -- normally 'out' but can be 'in' for order=out local multiple = out_unit_table.multiple -- table of scaling factors (will not be nil) local combos = out_unit_table.combination -- table of unit tables (will not be nil) local abbr = parms.abbr local abbr_org = parms.abbr_org local disp = parms.disp local want_name = (abbr_org == nil and (disp == 'or' or disp == 'slash')) or not (abbr == 'on' or abbr == inout or abbr == 'mos') local want_link = (parms.lk == 'on' or parms.lk == inout) local mid = parms.opt_flip and parms.mid or '' local sep1 = '&nbsp;' local sep2 = ' ' if parms.opt_adjectival and want_name then sep1 = '-' sep2 = '-' end local do_spell = parms.opt_spell_out parms.opt_spell_out = nil -- so the call to cvtround does not spell the value local function make_result(info, isfirst) local fmt, outvalue, sign local results = {} for i = 1, #combos do local tfrac, thisvalue, strforce local out_current = combos[i] out_current.inout = inout local scale = multiple[i] if i == 1 then -- least significant unit ('in' from 'ftin') local decimals out_current.frac = out_unit_table.frac local success, outinfo = cvtround(parms, info, in_unit_table, out_current) if not success then return false, outinfo end if isfirst then out_unit_table.valinfo = { outinfo } -- in case output value of first least significant unit is needed end sign = outinfo.sign tfrac = outinfo.fraction_table if outinfo.is_scientific then strforce = outinfo.show decimals = '' elseif tfrac then decimals = '' else local show = outinfo.show -- number as a string in local language local p1, p2 = show:find(numdot, 1, true) decimals = p1 and show:sub(p2 + 1) or '' -- text after numdot, if any end fmt = '%.' .. ulen(decimals) .. 'f' -- to reproduce precision if decimals == '' then if tfrac then outvalue = floor(outinfo.raw_absvalue) -- integer part only; fraction added later else outvalue = floor(outinfo.raw_absvalue + 0.5) -- keep all integer digits of least significant unit end else outvalue = outinfo.absvalue end end if scale then outvalue, thisvalue = divide(outvalue, scale) else thisvalue = outvalue end local id if want_name then if varname then local clean if strforce or tfrac then clean = '.1' -- dummy value to force name for floating point else clean = format(fmt, thisvalue) end id = variable_name(clean, out_current) else local key = 'name2' if parms.opt_adjectival then key = 'name1' elseif tfrac then if thisvalue == 0 then key = 'name1' end elseif parms.opt_singular then if 0 < thisvalue and thisvalue < 1.0001 then key = 'name1' end else if thisvalue == 1 then key = 'name1' end end id = out_current[key] end else id = out_current['symbol'] end if i == 1 and omit_separator(id) then -- Testing the id of the least significant unit should be sufficient. sep1 = '' sep2 = '' end if want_link then local link = out_current.link if link then id = make_link(link, id, out_current) end end local strval local spell_inout = (i == #combos or outvalue == 0) and inout or '' -- trick so the last value processed (first displayed) has uppercase, if requested if strforce and outvalue == 0 then sign = '' -- any sign is in strforce strval = strforce -- show small values in scientific notation; will only use least significant unit elseif tfrac then local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil strval = format_fraction(parms, spell_inout, false, wholestr, tfrac.numstr, tfrac.denstr, do_spell) else strval = (thisvalue == 0) and from_en('0') or with_separator(parms, format(fmt, thisvalue)) if do_spell then strval = spell_number(parms, spell_inout, strval) or strval end end table.insert(results, strval .. sep1 .. id) if outvalue == 0 then break end fmt = '%.0f' -- only least significant unit can have a non-integral value end local reversed, count = {}, #results for i = 1, count do reversed[i] = results[count + 1 - i] end return true, sign .. table.concat(reversed, sep2) end local valinfo = in_unit_table.valinfo local success, result = make_result(valinfo[1], true) if not success then return false, result end local range = parms.range if range then for i = 1, range.n do local success, result2 = make_result(valinfo[i+1]) if not success then return false, result2 end result = range_text(range[i], want_name, parms, result, result2, inout, {spaced=true}) end end return true, result .. mid end local function process(parms, in_unit_table, out_unit_table) -- Return true, s, outunit where s = final wikitext result, -- or return false, t where t is an error message table. linked_pages = {} local success, bad_output local bad_input_mcode = in_unit_table.bad_mcode -- nil if input unit is a valid convert unit local out_unit = parms.out_unit if out_unit == nil or out_unit == '' or type(out_unit) == 'function' then if bad_input_mcode or parms.opt_input_unit_only then bad_output = '' else local getdef = type(out_unit) == 'function' and out_unit or get_default success, out_unit = getdef(in_unit_table.valinfo[1].value, in_unit_table) parms.out_unit = out_unit if not success then bad_output = out_unit end end end if not bad_output and not out_unit_table then success, out_unit_table = lookup(parms, out_unit, 'any_combination') if success then local mismatch = check_mismatch(in_unit_table, out_unit_table) if mismatch then bad_output = mismatch end else bad_output = out_unit_table end end local lhs, rhs local flipped = parms.opt_flip and not bad_input_mcode if bad_output then rhs = (bad_output == '') and '' or message(parms, bad_output) elseif parms.opt_input_unit_only then rhs = '' else local combos -- nil (for 'ft' or 'ftin'), or table of unit tables (for 'm ft') if not out_unit_table.multiple then -- nil/false ('ft' or 'm ft'), or table of factors ('ftin') combos = out_unit_table.combination end local frac = parms.frac -- nil or denominator of fraction for output values if frac then -- Apply fraction to the unit (if only one), or to non-SI units (if a combination), -- except that if a precision is also specified, the fraction only applies to -- the hand unit; that allows the following result: -- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands) -- However, the following is handled elsewhere as a special case: -- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in) if combos then local precision = parms.precision for _, unit in ipairs(combos) do if unit.builtin == 'hand' or (not precision and not unit.prefixes) then unit.frac = frac end end else out_unit_table.frac = frac end end local outputs = {} local imax = combos and #combos or 1 -- 1 (single unit) or number of unit tables if imax == 1 then parms.opt_order_out = nil -- only useful with an output combination end if not flipped and not parms.opt_order_out then -- Process left side first so any duplicate links (from lk=on) are suppressed -- on right. Example: {{convert|28|e9pc|e9ly|abbr=off|lk=on}} lhs = process_input(parms, in_unit_table) end for i = 1, imax do local success, item local out_current = combos and combos[i] or out_unit_table out_current.inout = 'out' if i == 1 then if imax > 1 and out_current.builtin == 'hand' then out_current.out_next = combos[2] -- built-in hand can influence next unit in a combination end if parms.opt_order_out then out_current.inout = 'in' end end if out_current.multiple then success, item = make_output_multiple(parms, in_unit_table, out_current) else success, item = make_output_single(parms, in_unit_table, out_current) end if not success then return false, item end outputs[i] = item end if parms.opt_order_out then lhs = outputs[1] table.remove(outputs, 1) end local sep = parms.table_joins and parms.table_joins[2] or parms.join_between rhs = table.concat(outputs, sep) end if flipped or not lhs then local input = process_input(parms, in_unit_table) if flipped then lhs = rhs rhs = input else lhs = input end end if parms.join_before then lhs = parms.join_before .. lhs end local wikitext if bad_input_mcode then if bad_input_mcode == '' then wikitext = lhs else wikitext = lhs .. message(parms, bad_input_mcode) end elseif parms.table_joins then wikitext = parms.table_joins[1] .. lhs .. parms.table_joins[2] .. rhs else wikitext = lhs .. parms.joins[1] .. rhs .. parms.joins[2] end if parms.warnings and not bad_input_mcode then wikitext = wikitext .. parms.warnings end return true, get_styles(parms) .. wikitext, out_unit_table end local function main_convert(frame) -- Do convert, and if needed, do it again with higher default precision. local parms = { frame = frame } -- will hold template arguments, after translation set_config(frame.args) local success, result = get_parms(parms, frame:getParent().args) if success then if type(result) ~= 'table' then return tostring(result) end local in_unit_table = result local out_unit_table for _ = 1, 2 do -- use counter so cannot get stuck repeating convert success, result, out_unit_table = process(parms, in_unit_table, out_unit_table) if success and parms.do_convert_again then parms.do_convert_again = false else break end end end -- If input=x gives a problem, the result should be just the user input -- (if x is a property like P123 it has been replaced with ''). -- An unknown input unit would display the input and an error message -- with success == true at this point. -- Also, can have success == false with a message that outputs an empty string. if parms.input_text then if success and not parms.have_problem then return result end local cat if parms.tracking then -- Add a tracking category using the given text as the category sort key. -- There is currently only one type of tracking, but in principle multiple -- items could be tracked, using different sort keys for convenience. cat = wanted_category('tracking', parms.tracking) end return parms.input_text .. (cat or '') end return success and result or message(parms, result) end local function _unit(unitcode, options) -- Helper function for Module:Val to look up a unit. -- Parameter unitcode must be a string to identify the wanted unit. -- Parameter options must be nil or a table with optional fields: -- value = number (for sort key; default value is 1) -- scaled_top = nil for a normal unit, or a number for a unit which is -- the denominator of a per unit (for sort key) -- si = { 'symbol', 'link' } -- (a table with two strings) to make an SI unit -- that will be used for the look up -- link = true if result should be [[linked]] -- sort = 'on' or 'debug' if result should include a sort key in a -- span element ('debug' makes the key visible) -- name = true for the name of the unit instead of the symbol -- us = true for the US spelling of the unit, if any -- Return nil if unitcode is not a non-empty string. -- Otherwise return a table with fields: -- text = requested symbol or name of unit, optionally linked -- scaled_value = input value adjusted by unit scale; used for sort key -- sortspan = span element with sort key like that provided by {{ntsh}}, -- calculated from the result of converting value -- to a base unit with scale 1. -- unknown = true if the unitcode was not known unitcode = strip(unitcode) if unitcode == nil or unitcode == '' then return nil end set_config({}) linked_pages = {} options = options or {} local parms = { abbr = options.name and 'off' or 'on', lk = options.link and 'on' or nil, opt_sp_us = options.us and true or nil, opt_ignore_error = true, -- do not add pages using this function to 'what links here' for Module:Convert/extra opt_sortable_on = options.sort == 'on' or options.sort == 'debug', opt_sortable_debug = options.sort == 'debug', } if options.si then -- Make a dummy table of units (just one unit) for lookup to use. -- This makes lookup recognize any SI prefix in the unitcode. local symbol = options.si[1] or '?' parms.unittable = { [symbol] = { _name1 = symbol, _name2 = symbol, _symbol = symbol, utype = symbol, scale = symbol == 'g' and 0.001 or 1, prefixes = 1, default = symbol, link = options.si[2], }} end local success, unit_table = lookup(parms, unitcode, 'no_combination') if not success then unit_table = setmetatable({ symbol = unitcode, name2 = unitcode, utype = unitcode, scale = 1, default = '', defkey = '', linkey = '' }, unit_mt) end local value = tonumber(options.value) or 1 local clean = tostring(abs(value)) local info = { value = value, altvalue = value, singular = (clean == '1'), clean = clean, show = clean, } unit_table.inout = 'in' unit_table.valinfo = { info } local sortspan, scaled_value if options.sort then sortspan, scaled_value = make_table_or_sort(parms, value, info, unit_table, options.scaled_top) end return { text = make_id(parms, 1, unit_table), sortspan = sortspan, scaled_value = scaled_value, unknown = not success and true or nil, } end return { convert = main_convert, _unit = _unit } cac541bea61c5fbbcb0a2768343935e97587b60a 1005 993 2023-07-10T16:51:35Z BigTa1k 2 1 revision imported Scribunto text/plain -- Convert a value from one unit of measurement to another. -- Example: {{convert|123|lb|kg}} --> 123 pounds (56 kg) -- See [[:en:Template:Convert/Transwiki guide]] if copying to another wiki. local MINUS = '−' -- Unicode U+2212 MINUS SIGN (UTF-8: e2 88 92) local abs = math.abs local floor = math.floor local format = string.format local log10 = math.log10 local ustring = mw.ustring local ulen = ustring.len local usub = ustring.sub -- Configuration options to keep magic values in one location. -- Conversion data and message text are defined in separate modules. local config, maxsigfig local numdot -- must be '.' or ',' or a character which works in a regex local numsep, numsep_remove, numsep_remove2 local data_code, all_units local text_code local varname -- can be a code to use variable names that depend on value local from_en_table -- to translate an output string of en digits to local language local to_en_table -- to translate an input string of digits in local language to en -- Use translation_table in convert/text to change the following. local en_default -- true uses lang=en unless convert has lang=local or local digits local group_method = 3 -- code for how many digits are in a group local per_word = 'per' -- for units like "liters per kilometer" local plural_suffix = 's' -- only other useful value is probably '' to disable plural unit names local omitsep -- true to omit separator before local symbol/name -- All units should be defined in the data module. However, to cater for quick changes -- and experiments, any unknown unit is looked up in an extra data module, if it exists. -- That module would be transcluded in only a small number of pages, so there should be -- little server overhead from making changes, and changes should propagate quickly. local extra_module -- name of module with extra units local extra_units -- nil or table of extra units from extra_module -- Some options in the invoking template can set variables used later in the module. local currency_text -- for a user-defined currency symbol: {{convert|12|$/ha|$=€}} (euro replaces dollar) local function from_en(text) -- Input is a string representing a number in en digits with '.' decimal mark, -- without digit grouping (which is done just after calling this). -- Return the translation of the string with numdot and digits in local language. if numdot ~= '.' then text = text:gsub('%.', numdot) end if from_en_table then text = text:gsub('%d', from_en_table) end return text end local function to_en(text) -- Input is a string representing a number in the local language with -- an optional numdot decimal mark and numsep digit grouping. -- Return the translation of the string with '.' mark and en digits, -- and no separators (they have to be removed here to handle cases like -- numsep = '.' and numdot = ',' with input "1.234.567,8"). if to_en_table then text = ustring.gsub(text, '%d', to_en_table) end if numsep_remove then text = text:gsub(numsep_remove, '') end if numsep_remove2 then text = text:gsub(numsep_remove2, '') end if numdot ~= '.' then text = text:gsub(numdot, '.') end return text end local function decimal_mark(text) -- Return ',' if text probably is using comma for decimal mark, or has no decimal mark. -- Return '.' if text probably is using dot for decimal mark. -- Otherwise return nothing (decimal mark not known). if not text:find('[.,]') then return ',' end text = text:gsub('^%-', ''):gsub('%+%d+/%d+$', ''):gsub('[Ee]%-?%d+$', '') local decimal = text:match('^0?([.,])%d+$') or text:match('%d([.,])%d?%d?$') or text:match('%d([.,])%d%d%d%d+$') if decimal then return decimal end if text:match('%.%d+%.') then return ',' end if text:match('%,%d+,') then return '.' end end local add_warning, with_separator -- forward declarations local function to_en_with_check(text, parms) -- Version of to_en() for a wiki using numdot = ',' and numsep = '.' to check -- text (an input number as a string) which might have been copied from enwiki. -- For example, in '1.234' the '.' could be a decimal mark or a group separator. -- From viwiki. if to_en_table then text = ustring.gsub(text, '%d', to_en_table) end if decimal_mark(text) == '.' then local original = text text = text:gsub(',', '') -- for example, interpret "1,234.5" as an enwiki value if parms then add_warning(parms, 0, 'cvt_enwiki_num', original, with_separator({}, text)) end else if numsep_remove then text = text:gsub(numsep_remove, '') end if numsep_remove2 then text = text:gsub(numsep_remove2, '') end if numdot ~= '.' then text = text:gsub(numdot, '.') end end return text end local function omit_separator(id) -- Return true if there should be no separator before id (a unit symbol or name). -- For zhwiki, there should be no separator if id uses local characters. -- The following kludge should be a sufficient test. if omitsep then if id:sub(1, 2) == '-{' then -- for "-{...}-" content language variant return true end if id:byte() > 127 then local first = usub(id, 1, 1) if first ~= 'Å' and first ~= '°' and first ~= 'µ' then return true end end end return id:sub(1, 1) == '/' -- no separator before units like "/ha" end local spell_module -- name of module that can spell numbers local speller -- function from that module to handle spelling (set if needed) local wikidata_module, wikidata_data_module -- names of Wikidata modules local wikidata_code, wikidata_data -- exported tables from those modules (set if needed) local function set_config(args) -- Set configuration options from template #invoke or defaults. config = args maxsigfig = config.maxsigfig or 14 -- maximum number of significant figures local data_module, text_module local sandbox = config.sandbox and ('/' .. config.sandbox) or '' data_module = "Module:Convert/data" .. sandbox text_module = "Module:Convert/text" .. sandbox extra_module = "Module:Convert/extra" .. sandbox wikidata_module = "Module:Convert/wikidata" .. sandbox wikidata_data_module = "Module:Convert/wikidata/data" .. sandbox spell_module = "Module:ConvertNumeric" data_code = mw.loadData(data_module) text_code = mw.loadData(text_module) all_units = data_code.all_units local translation = text_code.translation_table if translation then numdot = translation.numdot numsep = translation.numsep if numdot == ',' and numsep == '.' then if text_code.all_messages.cvt_enwiki_num then to_en = to_en_with_check end end if translation.group then group_method = translation.group end if translation.per_word then per_word = translation.per_word end if translation.plural_suffix then plural_suffix = translation.plural_suffix end varname = translation.varname from_en_table = translation.from_en local use_workaround = true if use_workaround then -- 2013-07-05 workaround bug by making a copy of the required table. -- mw.ustring.gsub fails with a table (to_en_table) as the replacement, -- if the table is accessed via mw.loadData. local source = translation.to_en if source then to_en_table = {} for k, v in pairs(source) do to_en_table[k] = v end end else to_en_table = translation.to_en end if translation.lang == 'en default' then en_default = true -- for hiwiki end omitsep = translation.omitsep -- for zhwiki end numdot = config.numdot or numdot or '.' -- decimal mark before fractional digits numsep = config.numsep or numsep or ',' -- group separator for numbers -- numsep should be ',' or '.' or '' or '&nbsp;' or a Unicode character. -- numsep_remove must work in a regex to identify separators to be removed. if numsep ~= '' then numsep_remove = (numsep == '.') and '%.' or numsep end if numsep ~= ',' and numdot ~= ',' then numsep_remove2 = ',' -- so numbers copied from enwiki will work end end local function collection() -- Return a table to hold items. return { n = 0, add = function (self, item) self.n = self.n + 1 self[self.n] = item end, } end local function divide(numerator, denominator) -- Return integers quotient, remainder resulting from dividing the two -- given numbers, which should be unsigned integers. local quotient, remainder = floor(numerator / denominator), numerator % denominator if not (0 <= remainder and remainder < denominator) then -- Floating point limits may need this, as in {{convert|160.02|Ym|ydftin}}. remainder = 0 end return quotient, remainder end local function split(text, delimiter) -- Return a numbered table with fields from splitting text. -- The delimiter is used in a regex without escaping (for example, '.' would fail). -- Each field has any leading/trailing whitespace removed. local t = {} text = text .. delimiter -- to get last item for item in text:gmatch('%s*(.-)%s*' .. delimiter) do table.insert(t, item) end return t end local function strip(text) -- If text is a string, return its content with no leading/trailing -- whitespace. Otherwise return nil (a nil argument gives a nil result). if type(text) == 'string' then return text:match("^%s*(.-)%s*$") end end local function table_len(t) -- Return length (<100) of a numbered table to replace #t which is -- documented to not work if t is accessed via mw.loadData(). for i = 1, 100 do if t[i] == nil then return i - 1 end end end local function wanted_category(catkey, catsort, want_warning) -- Return message category if it is wanted in current namespace, -- otherwise return ''. local cat local title = mw.title.getCurrentTitle() if title then local nsdefault = '0' -- default namespace: '0' = article; '0,10' = article and template local namespace = title.namespace for _, v in ipairs(split(config.nscat or nsdefault, ',')) do if namespace == tonumber(v) then cat = text_code.all_categories[want_warning and 'warning' or catkey] if catsort and catsort ~= '' and cat:sub(-2) == ']]' then cat = cat:sub(1, -3) .. '|' .. mw.text.nowiki(usub(catsort, 1, 20)) .. ']]' end break end end end return cat or '' end local function message(parms, mcode, is_warning) -- Return wikitext for an error message, including category if specified -- for the message type. -- mcode = numbered table specifying the message: -- mcode[1] = 'cvt_xxx' (string used as a key to get message info) -- mcode[2] = 'parm1' (string to replace '$1' if any in message) -- mcode[3] = 'parm2' (string to replace '$2' if any in message) -- mcode[4] = 'parm3' (string to replace '$3' if any in message) local msg if type(mcode) == 'table' then if mcode[1] == 'cvt_no_output' then -- Some errors should cause convert to output an empty string, -- for example, for an optional field in an infobox. return '' end msg = text_code.all_messages[mcode[1]] end parms.have_problem = true local function subparm(fmt, ...) local rep = {} for i, v in ipairs({...}) do rep['$' .. i] = v end return (fmt:gsub('$%d+', rep)) end if msg then local parts = {} local regex, replace = msg.regex, msg.replace for i = 1, 3 do local limit = 40 local s = mcode[i + 1] if s then if regex and replace then s = s:gsub(regex, replace) limit = nil -- allow long "should be" messages end -- Escape user input so it does not break the message. -- To avoid tags (like {{convert|1<math>23</math>|m}}) breaking -- the mouseover title, any strip marker starting with char(127) is -- replaced with '...' (text not needing i18n). local append local pos = s:find(string.char(127), 1, true) if pos then append = '...' s = s:sub(1, pos - 1) end if limit and ulen(s) > limit then s = usub(s, 1, limit) append = '...' end s = mw.text.nowiki(s) .. (append or '') else s = '?' end parts['$' .. i] = s end local function ispreview() -- Return true if a prominent message should be shown. if parms.test == 'preview' or parms.test == 'nopreview' then -- For testing, can preview a real message or simulate a preview -- when running automated tests. return parms.test == 'preview' end local success, revid = pcall(function () return (parms.frame):preprocess('{{REVISIONID}}') end) return success and (revid == '') end local want_warning = is_warning and not config.warnings and -- show unobtrusive warnings if config.warnings not configured not msg.nowarn -- but use msg settings, not standard warning, if specified local title = string.gsub(msg[1] or 'Missing message', '$%d+', parts) local text = want_warning and '*' or msg[2] or 'Missing message' local cat = wanted_category(msg[3], mcode[2], want_warning) local anchor = msg[4] or '' local fmtkey = ispreview() and 'cvt_format_preview' or (want_warning and 'cvt_format2' or msg.format or 'cvt_format') local fmt = text_code.all_messages[fmtkey] or 'convert: bug' return subparm(fmt, title:gsub('"', '&quot;'), text, cat, anchor) end return 'Convert internal error: unknown message' end function add_warning(parms, level, key, text1, text2) -- for forward declaration above -- If enabled, add a warning that will be displayed after the convert result. -- A higher level is more verbose: more kinds of warnings are displayed. -- To reduce output noise, only the first warning is displayed. if level <= (tonumber(config.warnings) or 1) then if parms.warnings == nil then parms.warnings = message(parms, { key, text1, text2 }, true) end end end local function spell_number(parms, inout, number, numerator, denominator) -- Return result of spelling (number, numerator, denominator), or -- return nil if spelling is not available or not supported for given text. -- Examples (each value must be a string or nil): -- number numerator denominator output -- ------ --------- ----------- ------------------- -- "1.23" nil nil one point two three -- "1" "2" "3" one and two thirds -- nil "2" "3" two thirds if not speller then local function get_speller(module) return require(module).spell_number end local success success, speller = pcall(get_speller, spell_module) if not success or type(speller) ~= 'function' then add_warning(parms, 1, 'cvt_no_spell', 'spell') return nil end end local case if parms.spell_upper == inout then case = true parms.spell_upper = nil -- only uppercase first word in a multiple unit end local sp = not parms.opt_sp_us local adj = parms.opt_adjectival return speller(number, numerator, denominator, case, sp, adj) end ------------------------------------------------------------------------ -- BEGIN: Code required only for built-in units. -- LATER: If need much more code, move to another module to simplify this module. local function speed_of_sound(altitude) -- This is for the Mach built-in unit of speed. -- Return speed of sound in metres per second at given altitude in feet. -- If no altitude given, use default (zero altitude = sea level). -- Table gives speed of sound in miles per hour at various altitudes: -- altitude = -17,499 to 402,499 feet -- mach_table[a + 4] = s where -- a = (altitude / 5000) rounded to nearest integer (-3 to 80) -- s = speed of sound (mph) at that altitude -- LATER: Should calculate result from an interpolation between the next -- lower and higher altitudes in table, rather than rounding to nearest. -- From: http://www.aerospaceweb.org/question/atmosphere/q0112.shtml local mach_table = { -- a = 799.5, 787.0, 774.2, 761.207051, -- -3 to 0 748.0, 734.6, 721.0, 707.0, 692.8, 678.3, 663.5, 660.1, 660.1, 660.1, -- 1 to 10 660.1, 660.1, 660.1, 662.0, 664.3, 666.5, 668.9, 671.1, 673.4, 675.6, -- 11 to 20 677.9, 683.7, 689.9, 696.0, 702.1, 708.1, 714.0, 719.9, 725.8, 731.6, -- 21 to 30 737.3, 737.7, 737.7, 736.2, 730.5, 724.6, 718.8, 712.9, 707.0, 701.0, -- 31 to 40 695.0, 688.9, 682.8, 676.6, 670.4, 664.1, 657.8, 652.9, 648.3, 643.7, -- 41 to 50 639.1, 634.4, 629.6, 624.8, 620.0, 615.2, 613.2, 613.2, 613.2, 613.5, -- 51 to 60 614.4, 615.3, 616.7, 619.8, 623.4, 629.7, 635.0, 641.1, 650.6, 660.0, -- 61 to 70 672.5, 674.3, 676.1, 677.9, 679.7, 681.5, 683.3, 685.1, 686.8, 688.6, -- 71 to 80 } altitude = altitude or 0 local a = (altitude < 0) and -altitude or altitude a = floor(a / 5000 + 0.5) if altitude < 0 then a = -a end if a < -3 then a = -3 elseif a > 80 then a = 80 end return mach_table[a + 4] * 0.44704 -- mph converted to m/s end -- END: Code required only for built-in units. ------------------------------------------------------------------------ local function add_style(parms, class) -- Add selected template style to parms if not already present. parms.templatestyles = parms.templatestyles or {} if not parms.templatestyles[class] then parms.templatestyles[class] = parms.frame:extensionTag({ name = 'templatestyles', args = { src = text_code.titles[class] } }) end end local function get_styles(parms) -- Return string of required template styles, empty if none. if parms.templatestyles then local t = {} for _, v in pairs(parms.templatestyles) do table.insert(t, v) end return table.concat(t) end return '' end local function get_range(word) -- Return a range (string or table) corresponding to word (like "to"), -- or return nil if not a range word. local ranges = text_code.ranges return ranges.types[word] or ranges.types[ranges.aliases[word]] end local function check_mismatch(unit1, unit2) -- If unit1 cannot be converted to unit2, return an error message table. -- This allows conversion between units of the same type, and between -- Nm (normally torque) and ftlb (energy), as in gun-related articles. -- This works because Nm is the base unit (scale = 1) for both the -- primary type (torque), and the alternate type (energy, where Nm = J). -- A match occurs if the primary types are the same, or if unit1 matches -- the alternate type of unit2, and vice versa. That provides a whitelist -- of which conversions are permitted between normally incompatible types. if unit1.utype == unit2.utype or (unit1.utype == unit2.alttype and unit1.alttype == unit2.utype) then return nil end return { 'cvt_mismatch', unit1.utype, unit2.utype } end local function override_from(out_table, in_table, fields) -- Copy the specified fields from in_table to out_table, but do not -- copy nil fields (keep any corresponding field in out_table). for _, field in ipairs(fields) do if in_table[field] then out_table[field] = in_table[field] end end end local function shallow_copy(t) -- Return a shallow copy of table t. -- Do not need the features and overhead of the Scribunto mw.clone(). local result = {} for k, v in pairs(t) do result[k] = v end return result end local unit_mt = { -- Metatable to get missing values for a unit that does not accept SI prefixes. -- Warning: The boolean value 'false' is returned for any missing field -- so __index is not called twice for the same field in a given unit. __index = function (self, key) local value if key == 'name1' or key == 'sym_us' then value = self.symbol elseif key == 'name2' then value = self.name1 .. plural_suffix elseif key == 'name1_us' then value = self.name1 if not rawget(self, 'name2_us') then -- If name1_us is 'foot', do not make name2_us by appending plural_suffix. self.name2_us = self.name2 end elseif key == 'name2_us' then local raw1_us = rawget(self, 'name1_us') if raw1_us then value = raw1_us .. plural_suffix else value = self.name2 end elseif key == 'link' then value = self.name1 else value = false end rawset(self, key, value) return value end } local function prefixed_name(unit, name, index) -- Return unit name with SI prefix inserted at correct position. -- index = 1 (name1), 2 (name2), 3 (name1_us), 4 (name2_us). -- The position is a byte (not character) index, so use Lua's sub(). local pos = rawget(unit, 'prefix_position') if type(pos) == 'string' then pos = tonumber(split(pos, ',')[index]) end if pos then return name:sub(1, pos - 1) .. unit.si_name .. name:sub(pos) end return unit.si_name .. name end local unit_prefixed_mt = { -- Metatable to get missing values for a unit that accepts SI prefixes. -- Before use, fields si_name, si_prefix must be defined. -- The unit must define _symbol, _name1 and -- may define _sym_us, _name1_us, _name2_us -- (_sym_us, _name2_us may be defined for a language using sp=us -- to refer to a variant unrelated to U.S. units). __index = function (self, key) local value if key == 'symbol' then value = self.si_prefix .. self._symbol if value == 'l' then value = 'L' end elseif key == 'sym_us' then value = rawget(self, '_sym_us') if value then value = self.si_prefix .. value else value = self.symbol end elseif key == 'name1' then value = prefixed_name(self, self._name1, 1) elseif key == 'name2' then value = rawget(self, '_name2') if value then value = prefixed_name(self, value, 2) else value = self.name1 .. plural_suffix end elseif key == 'name1_us' then value = rawget(self, '_name1_us') if value then value = prefixed_name(self, value, 3) else value = self.name1 end elseif key == 'name2_us' then value = rawget(self, '_name2_us') if value then value = prefixed_name(self, value, 4) elseif rawget(self, '_name1_us') then value = self.name1_us .. plural_suffix else value = self.name2 end elseif key == 'link' then value = self.name1 else value = false end rawset(self, key, value) return value end } local unit_per_mt = { -- Metatable to get values for a per unit of form "x/y". -- This is never called to determine a unit name or link because per units -- are handled as a special case. -- Similarly, the default output is handled elsewhere, and for a symbol -- this is only called from get_default() for default_exceptions. __index = function (self, key) local value if key == 'symbol' then local per = self.per local unit1, unit2 = per[1], per[2] if unit1 then value = unit1[key] .. '/' .. unit2[key] else value = '/' .. unit2[key] end elseif key == 'sym_us' then value = self.symbol elseif key == 'scale' then local per = self.per local unit1, unit2 = per[1], per[2] value = (unit1 and unit1.scale or 1) * self.scalemultiplier / unit2.scale else value = false end rawset(self, key, value) return value end } local function make_per(unitcode, unit_table, ulookup) -- Return true, t where t is a per unit with unit codes expanded to unit tables, -- or return false, t where t is an error message table. local result = { unitcode = unitcode, utype = unit_table.utype, per = {} } override_from(result, unit_table, { 'invert', 'iscomplex', 'default', 'link', 'symbol', 'symlink' }) result.symbol_raw = (result.symbol or false) -- to distinguish between a defined exception and a metatable calculation local prefix for i, v in ipairs(unit_table.per) do if i == 1 and v == '' then -- First unit symbol can be empty; that gives a nil first unit table. elseif i == 1 and text_code.currency[v] then prefix = currency_text or v else local success, t = ulookup(v) if not success then return false, t end result.per[i] = t end end local multiplier = unit_table.multiplier if not result.utype then -- Creating an automatic per unit. local unit1 = result.per[1] local utype = (unit1 and unit1.utype or prefix or '') .. '/' .. result.per[2].utype local t = data_code.per_unit_fixups[utype] if t then if type(t) == 'table' then utype = t.utype or utype result.link = result.link or t.link multiplier = multiplier or t.multiplier else utype = t end end result.utype = utype end result.scalemultiplier = multiplier or 1 result.vprefix = prefix or false -- set to non-nil to avoid calling __index return true, setmetatable(result, unit_per_mt) end local function lookup(parms, unitcode, what, utable, fails, depth) -- Return true, t where t is a copy of the unit's converter table, -- or return false, t where t is an error message table. -- Parameter 'what' determines whether combination units are accepted: -- 'no_combination' : single unit only -- 'any_combination' : single unit or combination or output multiple -- 'only_multiple' : single unit or output multiple only -- Parameter unitcode is a symbol (like 'g'), with an optional SI prefix (like 'kg'). -- If, for example, 'kg' is in this table, that entry is used; -- otherwise the prefix ('k') is applied to the base unit ('g'). -- If unitcode is a known combination code (and if allowed by what), -- a table of output multiple unit tables is included in the result. -- For compatibility with the old template, an underscore in a unitcode is -- replaced with a space so usage like {{convert|350|board_feet}} works. -- Wikignomes may also put two spaces or "&nbsp;" in combinations, so -- replace underscore, "&nbsp;", and multiple spaces with a single space. utable = utable or parms.unittable or all_units fails = fails or {} depth = depth and depth + 1 or 1 if depth > 9 then -- There are ways to mistakenly define units which result in infinite -- recursion when lookup() is called. That gives a long delay and very -- confusing error messages, so the depth parameter is used as a guard. return false, { 'cvt_lookup', unitcode } end if unitcode == nil or unitcode == '' then return false, { 'cvt_no_unit' } end unitcode = unitcode:gsub('_', ' '):gsub('&nbsp;', ' '):gsub(' +', ' ') local function call_make_per(t) return make_per(unitcode, t, function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end ) end local t = utable[unitcode] if t then if t.shouldbe then return false, { 'cvt_should_be', t.shouldbe } end if t.sp_us then parms.opt_sp_us = true end local target = t.target -- nil, or unitcode is an alias for this target if target then local success, result = lookup(parms, target, what, utable, fails, depth) if not success then return false, result end override_from(result, t, { 'customary', 'default', 'link', 'symbol', 'symlink' }) local multiplier = t.multiplier if multiplier then result.multiplier = tostring(multiplier) result.scale = result.scale * multiplier end return true, result end if t.per then return call_make_per(t) end local combo = t.combination -- nil or a table of unitcodes if combo then local multiple = t.multiple if what == 'no_combination' or (what == 'only_multiple' and not multiple) then return false, { 'cvt_bad_unit', unitcode } end -- Recursively create a combination table containing the -- converter table of each unitcode. local result = { utype = t.utype, multiple = multiple, combination = {} } local cvt = result.combination for i, v in ipairs(combo) do local success, t = lookup(parms, v, multiple and 'no_combination' or 'only_multiple', utable, fails, depth) if not success then return false, t end cvt[i] = t end return true, result end local result = shallow_copy(t) result.unitcode = unitcode if result.prefixes then result.si_name = '' result.si_prefix = '' return true, setmetatable(result, unit_prefixed_mt) end return true, setmetatable(result, unit_mt) end local SIprefixes = text_code.SIprefixes for plen = SIprefixes[1] or 2, 1, -1 do -- Look for an SI prefix; should never occur with an alias. -- Check for longer prefix first ('dam' is decametre). -- SIprefixes[1] = prefix maximum #characters (as seen by mw.ustring.sub). local prefix = usub(unitcode, 1, plen) local si = SIprefixes[prefix] if si then local t = utable[usub(unitcode, plen+1)] if t and t.prefixes then local result = shallow_copy(t) result.unitcode = unitcode result.si_name = parms.opt_sp_us and si.name_us or si.name result.si_prefix = si.prefix or prefix result.scale = t.scale * 10 ^ (si.exponent * t.prefixes) return true, setmetatable(result, unit_prefixed_mt) end end end -- Accept user-defined combinations like "acre+m2+ha" or "acre m2 ha" for output. -- If '+' is used, each unit code can include a space, and any error is fatal. -- If ' ' is used and if each space-separated word is a unit code, it is a combo, -- but errors are not fatal so the unit code can be looked up as an extra unit. local err_is_fatal local combo = collection() if unitcode:find('+', 1, true) then err_is_fatal = true for item in (unitcode .. '+'):gmatch('%s*(.-)%s*%+') do if item ~= '' then combo:add(item) end end elseif unitcode:find('%s') then for item in unitcode:gmatch('%S+') do combo:add(item) end end if combo.n > 1 then local function lookup_combo() if what == 'no_combination' or what == 'only_multiple' then return false, { 'cvt_bad_unit', unitcode } end local result = { combination = {} } local cvt = result.combination for i, v in ipairs(combo) do local success, t = lookup(parms, v, 'only_multiple', utable, fails, depth) if not success then return false, t end if i == 1 then result.utype = t.utype else local mismatch = check_mismatch(result, t) if mismatch then return false, mismatch end end cvt[i] = t end return true, result end local success, result = lookup_combo() if success or err_is_fatal then return success, result end end -- Accept any unit with an engineering notation prefix like "e6cuft" -- (million cubic feet), but not chained prefixes like "e3e6cuft", -- and not if the unit is a combination or multiple, -- and not if the unit has an offset or is a built-in. -- Only en digits are accepted. local exponent, baseunit = unitcode:match('^e(%d+)(.*)') if exponent then local engscale = text_code.eng_scales[exponent] if engscale then local success, result = lookup(parms, baseunit, 'no_combination', utable, fails, depth) if success and not (result.offset or result.builtin or result.engscale) then result.unitcode = unitcode -- 'e6cuft' not 'cuft' result.defkey = unitcode -- key to lookup default exception result.engscale = engscale result.scale = result.scale * 10 ^ tonumber(exponent) return true, result end end end -- Look for x/y; split on right-most slash to get scale correct (x/y/z is x/y per z). local top, bottom = unitcode:match('^(.-)/([^/]+)$') if top and not unitcode:find('e%d') then -- If valid, create an automatic per unit for an "x/y" unit code. -- The unitcode must not include extraneous spaces. -- Engineering notation (apart from at start and which has been stripped before here), -- is not supported so do not make a per unit if find text like 'e3' in unitcode. local success, result = call_make_per({ per = {top, bottom} }) if success then return true, result end end if not parms.opt_ignore_error and not get_range(unitcode) then -- Want the "what links here" list for the extra_module to show only cases -- where an extra unit is used, so do not require it if invoked from {{val}} -- or if looking up a range word which cannot be a unit. if not extra_units then local success, extra = pcall(function () return require(extra_module).extra_units end) if success and type(extra) == 'table' then extra_units = extra end end if extra_units then -- A unit in one data table might refer to a unit in the other table, so -- switch between them, relying on fails or depth to terminate loops. if not fails[unitcode] then fails[unitcode] = true local other = (utable == all_units) and extra_units or all_units local success, result = lookup(parms, unitcode, what, other, fails, depth) if success then return true, result end end end end if to_en_table then -- At fawiki it is common to translate all digits so a unit like "km2" becomes "km۲". local en_code = ustring.gsub(unitcode, '%d', to_en_table) if en_code ~= unitcode then return lookup(parms, en_code, what, utable, fails, depth) end end return false, { 'cvt_unknown', unitcode } end local function valid_number(num) -- Return true if num is a valid number. -- In Scribunto (different from some standard Lua), when expressed as a string, -- overflow or other problems are indicated with text like "inf" or "nan" -- which are regarded as invalid here (each contains "n"). if type(num) == 'number' and tostring(num):find('n', 1, true) == nil then return true end end local function hyphenated(name, parts) -- Return a hyphenated form of given name (for adjectival usage). -- The name may be linked and the target of the link must not be changed. -- Hypothetical examples: -- [[long ton|ton]] → [[long ton|ton]] (no change) -- [[tonne|long ton]] → [[tonne|long-ton]] -- [[metric ton|long ton]] → [[metric ton|long-ton]] -- [[long ton]] → [[long ton|long-ton]] -- Input can also have multiple links in a single name like: -- [[United States customary units|U.S.]] [[US gallon|gallon]] -- [[mile]]s per [[United States customary units|U.S.]] [[quart]] -- [[long ton]]s per [[short ton]] -- Assume that links cannot be nested (never like "[[abc[[def]]ghi]]"). -- This uses a simple and efficient procedure that works for most cases. -- Some units (if used) would require more, and can later think about -- adding a method to handle exceptions. -- The procedure is to replace each space with a hyphen, but -- not a space after ')' [for "(pre-1954&nbsp;US) nautical mile"], and -- not spaces immediately before '(' or in '(...)' [for cases like -- "British thermal unit (ISO)" and "Calorie (International Steam Table)"]. if name:find(' ', 1, true) then if parts then local pos if name:sub(1, 1) == '(' then pos = name:find(')', 1, true) if pos then return name:sub(1, pos+1) .. name:sub(pos+2):gsub(' ', '-') end elseif name:sub(-1) == ')' then pos = name:find('(', 1, true) if pos then return name:sub(1, pos-2):gsub(' ', '-') .. name:sub(pos-1) end end return name:gsub(' ', '-') end parts = collection() for before, item, after in name:gmatch('([^[]*)(%[%[[^[]*%]%])([^[]*)') do if item:find(' ', 1, true) then local prefix local plen = item:find('|', 1, true) if plen then prefix = item:sub(1, plen) item = item:sub(plen + 1, -3) else prefix = item:sub(1, -3) .. '|' item = item:sub(3, -3) end item = prefix .. hyphenated(item, parts) .. ']]' end parts:add(before:gsub(' ', '-') .. item .. after:gsub(' ', '-')) end if parts.n == 0 then -- No link like "[[...]]" was found in the original name. parts:add(hyphenated(name, parts)) end return table.concat(parts) end return name end local function hyphenated_maybe(parms, want_name, sep, id, inout) -- Return s, f where -- s = id, possibly modified -- f = true if hyphenated -- Possible modifications: hyphenate; prepend '-'; append mid text. if id == nil or id == '' then return '' end local mid = (inout == (parms.opt_flip and 'out' or 'in')) and parms.mid or '' if want_name then if parms.opt_adjectival then return '-' .. hyphenated(id) .. mid, true end if parms.opt_add_s and id:sub(-1) ~= 's' then id = id .. 's' -- for nowiki end end return sep .. id .. mid end local function use_minus(text) -- Return text with Unicode minus instead of '-', if present. if text:sub(1, 1) == '-' then return MINUS .. text:sub(2) end return text end local function digit_groups(parms, text, method) -- Return a numbered table of groups of digits (left-to-right, in local language). -- Parameter method is a number or nil: -- 3 for 3-digit grouping (default), or -- 2 for 3-then-2 grouping (only for digits before decimal mark). local len_right local len_left = text:find('.', 1, true) if len_left then len_right = #text - len_left len_left = len_left - 1 else len_left = #text end local twos = method == 2 and len_left > 5 local groups = collection() local run = len_left local n if run < 4 or (run == 4 and parms.opt_comma5) then if parms.opt_gaps then n = run else n = #text end elseif twos then n = run % 2 == 0 and 1 or 2 else n = run % 3 == 0 and 3 or run % 3 end while run > 0 do groups:add(n) run = run - n n = (twos and run > 3) and 2 or 3 end if len_right then if groups.n == 0 then groups:add(0) end if parms.opt_gaps and len_right > 3 then local want4 = not parms.opt_gaps3 -- true gives no gap before trailing single digit local isfirst = true run = len_right while run > 0 do n = (want4 and run == 4) and 4 or (run > 3 and 3 or run) if isfirst then isfirst = false groups[groups.n] = groups[groups.n] + 1 + n else groups:add(n) end run = run - n end else groups[groups.n] = groups[groups.n] + 1 + len_right end end local pos = 1 for i, length in ipairs(groups) do groups[i] = from_en(text:sub(pos, pos + length - 1)) pos = pos + length end return groups end function with_separator(parms, text) -- for forward declaration above -- Input text is a number in en digits with optional '.' decimal mark. -- Return an equivalent, formatted for display: -- with a custom decimal mark instead of '.', if wanted -- with thousand separators inserted, if wanted -- digits in local language -- The given text is like '123' or '123.' or '12345.6789'. -- The text has no sign (caller inserts that later, if necessary). -- When using gaps, they are inserted before and after the decimal mark. -- Separators are inserted only before the decimal mark. -- A trailing dot (as in '123.') is removed because their use appears to -- be accidental, and such a number should be shown as '123' or '123.0'. -- It is useful for convert to suppress the dot so, for example, '4000.' -- is a simple way of indicating that all the digits are significant. if text:sub(-1) == '.' then text = text:sub(1, -2) end if #text < 4 or parms.opt_nocomma or numsep == '' then return from_en(text) end local groups = digit_groups(parms, text, group_method) if parms.opt_gaps then if groups.n <= 1 then return groups[1] or '' end local nowrap = '<span style="white-space: nowrap">' local gap = '<span style="margin-left: 0.25em">' local close = '</span>' return nowrap .. groups[1] .. gap .. table.concat(groups, close .. gap, 2, groups.n) .. close .. close end return table.concat(groups, numsep) end -- An input value like 1.23e12 is displayed using scientific notation (1.23×10¹²). -- That also makes the output use scientific notation, except for small values. -- In addition, very small or very large output values use scientific notation. -- Use format(fmtpower, significand, '10', exponent) where each argument is a string. local fmtpower = '%s<span style="margin:0 .15em 0 .25em">×</span>%s<sup>%s</sup>' local function with_exponent(parms, show, exponent) -- Return wikitext to display the implied value in scientific notation. -- Input uses en digits; output uses digits in local language. return format(fmtpower, with_separator(parms, show), from_en('10'), use_minus(from_en(tostring(exponent)))) end local function make_sigfig(value, sigfig) -- Return show, exponent that are equivalent to the result of -- converting the number 'value' (where value >= 0) to a string, -- rounded to 'sigfig' significant figures. -- The returned items are: -- show: a string of digits; no sign and no dot; -- there is an implied dot before show. -- exponent: a number (an integer) to shift the implied dot. -- Resulting value = tonumber('.' .. show) * 10^exponent. -- Examples: -- make_sigfig(23.456, 3) returns '235', 2 (.235 * 10^2). -- make_sigfig(0.0023456, 3) returns '235', -2 (.235 * 10^-2). -- make_sigfig(0, 3) returns '000', 1 (.000 * 10^1). if sigfig <= 0 then sigfig = 1 elseif sigfig > maxsigfig then sigfig = maxsigfig end if value == 0 then return string.rep('0', sigfig), 1 end local exp, fracpart = math.modf(log10(value)) if fracpart >= 0 then fracpart = fracpart - 1 exp = exp + 1 end local digits = format('%.0f', 10^(fracpart + sigfig)) if #digits > sigfig then -- Overflow (for sigfig=3: like 0.9999 rounding to "1000"; need "100"). digits = digits:sub(1, sigfig) exp = exp + 1 end assert(#digits == sigfig, 'Bug: rounded number has wrong length') return digits, exp end -- Fraction output format. local fracfmt = { { -- Like {{frac}} (fraction slash). '<span class="frac" role="math">{SIGN}<span class="num">{NUM}</span>&frasl;<span class="den">{DEN}</span></span>', -- 1/2 '<span class="frac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="num">{NUM}</span>&frasl;<span class="den">{DEN}</span></span>', -- 1+2/3 style = 'frac', }, { -- Like {{sfrac}} (stacked fraction, that is, horizontal bar). '<span class="sfrac tion" role="math">{SIGN}<span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span>', -- 1//2 '<span class="sfrac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="tion"><span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span></span>', -- 1+2//3 style = 'sfrac', }, } local function format_fraction(parms, inout, negative, wholestr, numstr, denstr, do_spell, style) -- Return wikitext for a fraction, possibly spelled. -- Inputs use en digits and have no sign; output uses digits in local language. local wikitext if not style then style = parms.opt_fraction_horizontal and 2 or 1 end if wholestr == '' then wholestr = nil end local substitute = { SIGN = negative and MINUS or '', WHOLE = wholestr and with_separator(parms, wholestr), NUM = from_en(numstr), DEN = from_en(denstr), } wikitext = fracfmt[style][wholestr and 2 or 1]:gsub('{(%u+)}', substitute) if do_spell then if negative then if wholestr then wholestr = '-' .. wholestr else numstr = '-' .. numstr end end local s = spell_number(parms, inout, wholestr, numstr, denstr) if s then return s end end add_style(parms, fracfmt[style].style) return wikitext end local function format_number(parms, show, exponent, isnegative) -- Parameter show is a string or a table containing strings. -- Each string is a formatted number in en digits and optional '.' decimal mark. -- A table represents a fraction: integer, numerator, denominator; -- if a table is given, exponent must be nil. -- Return t where t is a table with fields: -- show = wikitext formatted to display implied value -- (digits in local language) -- is_scientific = true if show uses scientific notation -- clean = unformatted show (possibly adjusted and with inserted '.') -- (en digits) -- sign = '' or MINUS -- exponent = exponent (possibly adjusted) -- The clean and exponent fields can be used to calculate the -- rounded absolute value, if needed. -- -- The value implied by the arguments is found from: -- exponent is nil; and -- show is a string of digits (no sign), with an optional dot; -- show = '123.4' is value 123.4, '1234' is value 1234.0; -- or: -- exponent is an integer indicating where dot should be; -- show is a string of digits (no sign and no dot); -- there is an implied dot before show; -- show does not start with '0'; -- show = '1234', exponent = 3 is value 0.1234*10^3 = 123.4. -- -- The formatted result: -- * Is for an output value and is spelled if wanted and possible. -- * Includes a Unicode minus if isnegative and not spelled. -- * Uses a custom decimal mark, if wanted. -- * Has digits grouped where necessary, if wanted. -- * Uses scientific notation if requested, or for very small or large values -- (which forces result to not be spelled). -- * Has no more than maxsigfig significant digits -- (same as old template and {{#expr}}). local xhi, xlo -- these control when scientific notation (exponent) is used if parms.opt_scientific then xhi, xlo = 4, 2 -- default for output if input uses e-notation elseif parms.opt_scientific_always then xhi, xlo = 0, 0 -- always use scientific notation (experimental) else xhi, xlo = 10, 4 -- default end local sign = isnegative and MINUS or '' local maxlen = maxsigfig local tfrac if type(show) == 'table' then tfrac = show show = tfrac.wholestr assert(exponent == nil, 'Bug: exponent given with fraction') end if not tfrac and not exponent then local integer, dot, decimals = show:match('^(%d*)(%.?)(.*)') if integer == '0' or integer == '' then local zeros, figs = decimals:match('^(0*)([^0]?.*)') if #figs == 0 then if #zeros > maxlen then show = '0.' .. zeros:sub(1, maxlen) end elseif #zeros >= xlo then show = figs exponent = -#zeros elseif #figs > maxlen then show = '0.' .. zeros .. figs:sub(1, maxlen) end elseif #integer >= xhi then show = integer .. decimals exponent = #integer else maxlen = maxlen + #dot if #show > maxlen then show = show:sub(1, maxlen) end end end if exponent then local function zeros(n) return string.rep('0', n) end if #show > maxlen then show = show:sub(1, maxlen) end if exponent > xhi or exponent <= -xlo or (exponent == xhi and show ~= '1' .. zeros(xhi - 1)) then -- When xhi, xlo = 10, 4 (the default), scientific notation is used if the -- rounded value satisfies: value >= 1e9 or value < 1e-4 (1e9 = 0.1e10), -- except if show is '1000000000' (1e9), for example: -- {{convert|1000000000|m|m|sigfig=10}} → 1,000,000,000 metres (1,000,000,000 m) local significand if #show > 1 then significand = show:sub(1, 1) .. '.' .. show:sub(2) else significand = show end return { clean = '.' .. show, exponent = exponent, sign = sign, show = sign .. with_exponent(parms, significand, exponent-1), is_scientific = true, } end if exponent >= #show then show = show .. zeros(exponent - #show) -- result has no dot elseif exponent <= 0 then show = '0.' .. zeros(-exponent) .. show else show = show:sub(1, exponent) .. '.' .. show:sub(exponent+1) end end local formatted_show if tfrac then show = tostring(tfrac.value) -- to set clean in returned table formatted_show = format_fraction(parms, 'out', isnegative, tfrac.wholestr, tfrac.numstr, tfrac.denstr, parms.opt_spell_out) else if isnegative and show:match('^0.?0*$') then sign = '' -- don't show minus if result is negative but rounds to zero end formatted_show = sign .. with_separator(parms, show) if parms.opt_spell_out then formatted_show = spell_number(parms, 'out', sign .. show) or formatted_show end end return { clean = show, sign = sign, show = formatted_show, is_scientific = false, -- to avoid calling __index } end local function extract_fraction(parms, text, negative) -- If text represents a fraction, return -- value, altvalue, show, denominator -- where -- value is a number (value of the fraction in argument text) -- altvalue is an alternate interpretation of any fraction for the hands -- unit where "12.1+3/4" means 12 hands 1.75 inches -- show is a string (formatted text for display of an input value, -- and is spelled if wanted and possible) -- denominator is value of the denominator in the fraction -- Otherwise, return nil. -- Input uses en digits and '.' decimal mark (input has been translated). -- Output uses digits in local language and local decimal mark, if any. ------------------------------------------------------------------------ -- Originally this function accepted x+y/z where x, y, z were any valid -- numbers, possibly with a sign. For example '1.23e+2+1.2/2.4' = 123.5, -- and '2-3/8' = 1.625. However, such usages were found to be errors or -- misunderstandings, so since August 2014 the following restrictions apply: -- x (if present) is an integer or has a single digit after decimal mark -- y and z are unsigned integers -- e-notation is not accepted -- The overall number can start with '+' or '-' (so '12+3/4' and '+12+3/4' -- and '-12-3/4' are valid). -- Any leading negative sign is removed by the caller, so only inputs -- like the following are accepted here (may have whitespace): -- negative = false false true (there was a leading '-') -- text = '2/3' '+2/3' '2/3' -- text = '1+2/3' '+1+2/3' '1-2/3' -- text = '12.3+1/2' '+12.3+1/2' '12.3-1/2' -- Values like '12.3+1/2' are accepted, but are intended only for use -- with the hands unit (not worth adding code to enforce that). ------------------------------------------------------------------------ local leading_plus, prefix, numstr, slashes, denstr = text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*(/+)%s*(%d+)%s*$') if not leading_plus then -- Accept a single U+2044 fraction slash because that may be pasted. leading_plus, prefix, numstr, denstr = text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*⁄%s*(%d+)%s*$') slashes = '/' end local numerator = tonumber(numstr) local denominator = tonumber(denstr) if numerator == nil or denominator == nil or (negative and leading_plus ~= '') then return nil end local whole, wholestr if prefix == '' then wholestr = '' whole = 0 else -- Any prefix must be like '12+' or '12-' (whole number and fraction sign); -- '12.3+' and '12.3-' are also accepted (single digit after decimal point) -- because '12.3+1/2 hands' is valid (12 hands 3½ inches). local num1, num2, frac_sign = prefix:match('^(%d+)(%.?%d?)%s*([+%-])$') if num1 == nil then return nil end if num2 == '' then -- num2 must be '' or like '.1' but not '.' or '.12' wholestr = num1 else if #num2 ~= 2 then return nil end wholestr = num1 .. num2 end if frac_sign ~= (negative and '-' or '+') then return nil end whole = tonumber(wholestr) if whole == nil then return nil end end local value = whole + numerator / denominator if not valid_number(value) then return nil end local altvalue = whole + numerator / (denominator * 10) local style = #slashes -- kludge: 1 or 2 slashes can be used to select style if style > 2 then style = 2 end local wikitext = format_fraction(parms, 'in', negative, leading_plus .. wholestr, numstr, denstr, parms.opt_spell_in, style) return value, altvalue, wikitext, denominator end local function extract_number(parms, text, another, no_fraction) -- Return true, info if can extract a number from text, -- where info is a table with the result, -- or return false, t where t is an error message table. -- Input can use en digits or digits in local language and can -- have references at the end. Accepting references is intended -- for use in infoboxes with a field for a value passed to convert. -- Parameter another = true if the expected value is not the first. -- Before processing, the input text is cleaned: -- * Any thousand separators (valid or not) are removed. -- * Any sign is replaced with '-' (if negative) or '' (otherwise). -- That replaces Unicode minus with '-'. -- If successful, the returned info table contains named fields: -- value = a valid number -- altvalue = a valid number, usually same as value but different -- if fraction used (for hands unit) -- singular = true if value is 1 or -1 (to use singular form of units) -- clean = cleaned text with any separators and sign removed -- (en digits and '.' decimal mark) -- show = text formatted for output, possibly with ref strip markers -- (digits in local language and custom decimal mark) -- The resulting show: -- * Is for an input value and is spelled if wanted and possible. -- * Has a rounded value, if wanted. -- * Has digits grouped where necessary, if wanted. -- * If negative, a Unicode minus is used; otherwise the sign is -- '+' (if the input text used '+'), or is '' (if no sign in input). text = strip(text or '') local reference local pos = text:find('\127', 1, true) if pos then local before = text:sub(1, pos - 1) local remainder = text:sub(pos) local refs = {} while #remainder > 0 do local ref, spaces ref, spaces, remainder = remainder:match('^(\127[^\127]*UNIQ[^\127]*%-ref[^\127]*\127)(%s*)(.*)') if ref then table.insert(refs, ref) else refs = {} break end end if #refs > 0 then text = strip(before) reference = table.concat(refs) end end local clean = to_en(text, parms) if clean == '' then return false, { another and 'cvt_no_num2' or 'cvt_no_num' } end local isnegative, propersign = false, '' -- most common case local singular, show, denominator local value = tonumber(clean) local altvalue if value then local sign = clean:sub(1, 1) if sign == '+' or sign == '-' then propersign = (sign == '+') and '+' or MINUS clean = clean:sub(2) end if value < 0 then isnegative = true value = -value end else local valstr for _, prefix in ipairs({ '-', MINUS, '&minus;' }) do -- Including '-' sets isnegative in case input is a fraction like '-2-3/4'. local plen = #prefix if clean:sub(1, plen) == prefix then valstr = clean:sub(plen + 1) if valstr:match('^%s') then -- "- 1" is invalid but "-1 - 1/2" is ok return false, { 'cvt_bad_num', text } end break end end if valstr then isnegative = true propersign = MINUS clean = valstr value = tonumber(clean) end if value == nil then if not no_fraction then value, altvalue, show, denominator = extract_fraction(parms, clean, isnegative) end if value == nil then return false, { 'cvt_bad_num', text } end if value <= 1 then singular = true -- for example, "½ mile" or "one half mile" (singular unit) end end end if not valid_number(value) then -- for example, "1e310" may overflow return false, { 'cvt_invalid_num' } end if show == nil then -- clean is a non-empty string with no spaces, and does not represent a fraction, -- and value = tonumber(clean) is a number >= 0. -- If the input uses e-notation, show will be displayed using a power of ten, but -- we use the number as given so it might not be normalized scientific notation. -- The input value is spelled if specified so any e-notation is ignored; -- that allows input like 2e6 to be spelled as "two million" which works -- because the spell module converts '2e6' to '2000000' before spelling. local function rounded(value, default, exponent) local precision = parms.opt_ri if precision then local fmt = '%.' .. format('%d', precision) .. 'f' local result = fmt:format(tonumber(value) + 2e-14) -- fudge for some common cases of bad rounding if not exponent then singular = (tonumber(result) == 1) end return result end return default end singular = (value == 1) local scientific local significand, exponent = clean:match('^([%d.]+)[Ee]([+%-]?%d+)') if significand then show = with_exponent(parms, rounded(significand, significand, exponent), exponent) scientific = true else show = with_separator(parms, rounded(value, clean)) end show = propersign .. show if parms.opt_spell_in then show = spell_number(parms, 'in', propersign .. rounded(value, clean)) or show scientific = false end if scientific then parms.opt_scientific = true end end if isnegative and (value ~= 0) then value = -value altvalue = -(altvalue or value) end return true, { value = value, altvalue = altvalue or value, singular = singular, clean = clean, show = show .. (reference or ''), denominator = denominator, } end local function get_number(text) -- Return v, f where: -- v = nil (text is not a number) -- or -- v = value of text (text is a number) -- f = true if value is an integer -- Input can use en digits or digits in local language or separators, -- but no Unicode minus, and no fraction. if text then local number = tonumber(to_en(text)) if number then local _, fracpart = math.modf(number) return number, (fracpart == 0) end end end local function gcd(a, b) -- Return the greatest common denominator for the given values, -- which are known to be positive integers. if a > b then a, b = b, a end if a <= 0 then return b end local r = b % a if r <= 0 then return a end if r == 1 then return 1 end return gcd(r, a) end local function fraction_table(value, denominator) -- Return value as a string or a table: -- * If result is a string, there is no fraction, and the result -- is value formatted as a string of en digits. -- * If result is a table, it represents a fraction with named fields: -- wholestr, numstr, denstr (strings of en digits for integer, numerator, denominator). -- The result is rounded to the nearest multiple of (1/denominator). -- If the multiple is zero, no fraction is included. -- No fraction is included if value is very large as the fraction would -- be unhelpful, particularly if scientific notation is required. -- Input value is a non-negative number. -- Input denominator is a positive integer for the desired fraction. if value <= 0 then return '0' end if denominator <= 0 or value > 1e8 then return format('%.2f', value) end local integer, decimals = math.modf(value) local numerator = floor((decimals * denominator) + 0.5 + 2e-14) -- add fudge for some common cases of bad rounding if numerator >= denominator then integer = integer + 1 numerator = 0 end local wholestr = tostring(integer) if numerator > 0 then local div = gcd(numerator, denominator) if div > 1 then numerator = numerator / div denominator = denominator / div end return { wholestr = (integer > 0) and wholestr or '', numstr = tostring(numerator), denstr = tostring(denominator), value = value, } end return wholestr end local function preunits(count, preunit1, preunit2) -- If count is 1: -- ignore preunit2 -- return p1 -- else: -- preunit1 is used for preunit2 if the latter is empty -- return p1, p2 -- where: -- p1 is text to insert before the input unit -- p2 is text to insert before the output unit -- p1 or p2 may be nil to mean "no preunit" -- Using '+' gives output like "5+ feet" (no space before, but space after). local function withspace(text, wantboth) -- Return text with space before and, if wantboth, after. -- However, no space is added if there is a space or '&nbsp;' or '-' -- at that position ('-' is for adjectival text). -- There is also no space if text starts with '&' -- (e.g. '&deg;' would display a degree symbol with no preceding space). local char = text:sub(1, 1) if char == '&' then return text -- an html entity can be used to specify the exact display end if not (char == ' ' or char == '-' or char == '+') then text = ' ' .. text end if wantboth then char = text:sub(-1, -1) if not (char == ' ' or char == '-' or text:sub(-6, -1) == '&nbsp;') then text = text .. ' ' end end return text end local PLUS = '+ ' preunit1 = preunit1 or '' local trim1 = strip(preunit1) if count == 1 then if trim1 == '' then return nil end if trim1 == '+' then return PLUS end return withspace(preunit1, true) end preunit1 = withspace(preunit1) preunit2 = preunit2 or '' local trim2 = strip(preunit2) if trim1 == '+' then if trim2 == '' or trim2 == '+' then return PLUS, PLUS end preunit1 = PLUS end if trim2 == '' then if trim1 == '' then return nil, nil end preunit2 = preunit1 elseif trim2 == '+' then preunit2 = PLUS elseif trim2 == '&#32;' then -- trick to make preunit2 empty preunit2 = nil else preunit2 = withspace(preunit2) end return preunit1, preunit2 end local function range_text(range, want_name, parms, before, after, inout, options) -- Return before .. rtext .. after -- where rtext is the text that separates two values in a range. local rtext, adj_text, exception options = options or {} if type(range) == 'table' then -- Table must specify range text for ('off' and 'on') or ('input' and 'output'), -- and may specify range text for 'adj=on', -- and may specify exception = true. rtext = range[want_name and 'off' or 'on'] or range[((inout == 'in') == (parms.opt_flip == true)) and 'output' or 'input'] adj_text = range['adj'] exception = range['exception'] else rtext = range end if parms.opt_adjectival then if want_name or (exception and parms.abbr_org == 'on') then rtext = adj_text or rtext:gsub(' ', '-'):gsub('&nbsp;', '-') end end if rtext == '–' and (options.spaced or after:sub(1, #MINUS) == MINUS) then rtext = '&nbsp;– ' end return before .. rtext .. after end local function get_composite(parms, iparm, in_unit_table) -- Look for a composite input unit. For example, {{convert|1|yd|2|ft|3|in}} -- would result in a call to this function with -- iparm = 3 (parms[iparm] = "2", just after the first unit) -- in_unit_table = (unit table for "yd"; contains value 1 for number of yards) -- Return true, iparm, unit where -- iparm = index just after the composite units (7 in above example) -- unit = composite unit table holding all input units, -- or return true if no composite unit is present in parms, -- or return false, t where t is an error message table. local default, subinfo local composite_units, count = { in_unit_table }, 1 local fixups = {} local total = in_unit_table.valinfo[1].value local subunit = in_unit_table while subunit.subdivs do -- subdivs is nil or a table of allowed subdivisions local subcode = strip(parms[iparm+1]) local subdiv = subunit.subdivs[subcode] or subunit.subdivs[(all_units[subcode] or {}).target] if not subdiv then break end local success success, subunit = lookup(parms, subcode, 'no_combination') if not success then return false, subunit end -- should never occur success, subinfo = extract_number(parms, parms[iparm]) if not success then return false, subinfo end iparm = iparm + 2 subunit.inout = 'in' subunit.valinfo = { subinfo } -- Recalculate total as a number of subdivisions. -- subdiv[1] = number of subdivisions per previous unit (integer > 1). total = total * subdiv[1] + subinfo.value if not default then -- set by the first subdiv with a default defined default = subdiv.default end count = count + 1 composite_units[count] = subunit if subdiv.unit or subdiv.name then fixups[count] = { unit = subdiv.unit, name = subdiv.name, valinfo = subunit.valinfo } end end if count == 1 then return true -- no error and no composite unit end for i, fixup in pairs(fixups) do local unit = fixup.unit local name = fixup.name if not unit or (count > 2 and name) then composite_units[i].fixed_name = name else local success, alternate = lookup(parms, unit, 'no_combination') if not success then return false, alternate end -- should never occur alternate.inout = 'in' alternate.valinfo = fixup.valinfo composite_units[i] = alternate end end return true, iparm, { utype = in_unit_table.utype, scale = subunit.scale, -- scale of last (least significant) unit valinfo = { { value = total, clean = subinfo.clean, denominator = subinfo.denominator } }, composite = composite_units, default = default or in_unit_table.default } end local function translate_parms(parms, kv_pairs) -- Update fields in parms by translating each key:value in kv_pairs to terms -- used by this module (may involve translating from local language to English). -- Also, checks are performed which may display warnings, if enabled. -- Return true if successful or return false, t where t is an error message table. currency_text = nil -- local testing can hold module in memory; must clear globals if kv_pairs.adj and kv_pairs.sing then -- For enwiki (before translation), warn if attempt to use adj and sing -- as the latter is a deprecated alias for the former. if kv_pairs.adj ~= kv_pairs.sing and kv_pairs.sing ~= '' then add_warning(parms, 1, 'cvt_unknown_option', 'sing=' .. kv_pairs.sing) end kv_pairs.sing = nil end kv_pairs.comma = kv_pairs.comma or config.comma -- for plwiki who want default comma=5 for loc_name, loc_value in pairs(kv_pairs) do local en_name = text_code.en_option_name[loc_name] if en_name then local en_value = text_code.en_option_value[en_name] if en_value == 'INTEGER' then -- altitude_ft, altitude_m, frac, sigfig en_value = nil if loc_value == '' then add_warning(parms, 2, 'cvt_empty_option', loc_name) else local minimum local number, is_integer = get_number(loc_value) if en_name == 'sigfig' then minimum = 1 elseif en_name == 'frac' then minimum = 2 if number and number < 0 then parms.opt_fraction_horizontal = true number = -number end else minimum = -1e6 end if number and is_integer and number >= minimum then en_value = number else local m if en_name == 'frac' then m = 'cvt_bad_frac' elseif en_name == 'sigfig' then m = 'cvt_bad_sigfig' else m = 'cvt_bad_altitude' end add_warning(parms, 1, m, loc_name .. '=' .. loc_value) end end elseif en_value == 'TEXT' then -- $, input, qid, qual, stylein, styleout, tracking en_value = loc_value ~= '' and loc_value or nil -- accept non-empty user text with no validation if not en_value and (en_name == '$' or en_name == 'qid' or en_name == 'qual') then add_warning(parms, 2, 'cvt_empty_option', loc_name) elseif en_name == '$' then -- Value should be a single character like "€" for the euro currency symbol, but anything is accepted. currency_text = (loc_value == 'euro') and '€' or loc_value elseif en_name == 'input' then -- May have something like {{convert|input=}} (empty input) if source is an infobox -- with optional fields. In that case, want to output nothing rather than an error. parms.input_text = loc_value -- keep input because parms.input is nil if loc_value == '' end else en_value = en_value[loc_value] if en_value and en_value:sub(-1) == '?' then en_value = en_value:sub(1, -2) add_warning(parms, -1, 'cvt_deprecated', loc_name .. '=' .. loc_value) end if en_value == nil then if loc_value == '' then add_warning(parms, 2, 'cvt_empty_option', loc_name) else add_warning(parms, 1, 'cvt_unknown_option', loc_name .. '=' .. loc_value) end elseif en_value == '' then en_value = nil -- an ignored option like adj=off elseif type(en_value) == 'string' and en_value:sub(1, 4) == 'opt_' then for _, v in ipairs(split(en_value, ',')) do local lhs, rhs = v:match('^(.-)=(.+)$') if rhs then parms[lhs] = tonumber(rhs) or rhs else parms[v] = true end end en_value = nil end end parms[en_name] = en_value else add_warning(parms, 1, 'cvt_unknown_option', loc_name .. '=' .. loc_value) end end local abbr_entered = parms.abbr local cfg_abbr = config.abbr if cfg_abbr then -- Don't warn if invalid because every convert would show that warning. if cfg_abbr == 'on always' then parms.abbr = 'on' elseif cfg_abbr == 'off always' then parms.abbr = 'off' elseif parms.abbr == nil then if cfg_abbr == 'on default' then parms.abbr = 'on' elseif cfg_abbr == 'off default' then parms.abbr = 'off' end end end if parms.abbr then if parms.abbr == 'unit' then parms.abbr = 'on' parms.number_word = true end parms.abbr_org = parms.abbr -- original abbr, before any flip elseif parms.opt_hand_hh then parms.abbr_org = 'on' parms.abbr = 'on' else parms.abbr = 'out' -- default is to abbreviate output only (use symbol, not name) end if parms.opt_order_out then -- Disable options that do not work in a useful way with order=out. parms.opt_flip = nil -- override adj=flip parms.opt_spell_in = nil parms.opt_spell_out = nil parms.opt_spell_upper = nil end if parms.opt_spell_out and not abbr_entered then parms.abbr = 'off' -- should show unit name when spelling the output value end if parms.opt_flip then local function swap_in_out(option) local value = parms[option] if value == 'in' then parms[option] = 'out' elseif value == 'out' then parms[option] = 'in' end end swap_in_out('abbr') swap_in_out('lk') if parms.opt_spell_in and not parms.opt_spell_out then -- For simplicity, and because it does not appear to be needed, -- user cannot set an option to spell the output only. parms.opt_spell_in = nil parms.opt_spell_out = true end end if parms.opt_spell_upper then parms.spell_upper = parms.opt_flip and 'out' or 'in' end if parms.opt_table or parms.opt_tablecen then if abbr_entered == nil and parms.lk == nil then parms.opt_values = true end parms.table_align = parms.opt_table and 'right' or 'center' end if parms.table_align or parms.opt_sortable_on then parms.need_table_or_sort = true end local disp_joins = text_code.disp_joins local default_joins = disp_joins['b'] parms.join_between = default_joins[3] or '; ' local disp = parms.disp if disp == nil then -- special case for the most common setting parms.joins = default_joins elseif disp == 'x' then -- Later, parms.joins is set from the input parameters. else -- Old template does this. local abbr = parms.abbr if disp == 'slash' then if abbr_entered == nil then disp = 'slash-nbsp' elseif abbr == 'in' or abbr == 'out' then disp = 'slash-sp' else disp = 'slash-nosp' end elseif disp == 'sqbr' then if abbr == 'on' then disp = 'sqbr-nbsp' else disp = 'sqbr-sp' end end parms.joins = disp_joins[disp] or default_joins parms.join_between = parms.joins[3] or parms.join_between parms.wantname = parms.joins.wantname end if (en_default and not parms.opt_lang_local and (parms[1] or ''):find('%d')) or parms.opt_lang_en then from_en_table = nil end if en_default and from_en_table then -- For hiwiki: localized symbol/name is defined with the US symbol/name field, -- and is used if output uses localized numbers. parms.opt_sp_us = true end return true end local function get_values(parms) -- If successful, update parms and return true, v, i where -- v = table of input values -- i = index to next entry in parms after those processed here -- or return false, t where t is an error message table. local valinfo = collection() -- numbered table of input values local range = collection() -- numbered table of range items (having, for example, 2 range items requires 3 input values) local had_nocomma -- true if removed "nocomma" kludge from second parameter (like "tonocomma") local parm2 = strip(parms[2]) if parm2 and parm2:sub(-7, -1) == 'nocomma' then parms[2] = strip(parm2:sub(1, -8)) parms.opt_nocomma = true had_nocomma = true end local function extractor(i) -- If the parameter is not a value, try unpacking it as a range ("1-23" for "1 to 23"). -- However, "-1-2/3" is a negative fraction (-1⅔), so it must be extracted first. -- Do not unpack a parameter if it is like "3-1/2" which is sometimes incorrectly -- used instead of "3+1/2" (and which should not be interpreted as "3 to ½"). -- Unpacked items are inserted into the parms table. -- The tail recursion allows combinations like "1x2 to 3x4". local valstr = strip(parms[i]) -- trim so any '-' as a negative sign will be at start local success, result = extract_number(parms, valstr, i > 1) if not success and valstr and i < 20 then -- check i to limit abuse local lhs, sep, rhs = valstr:match('^(%S+)%s+(%S+)%s+(%S.*)') if lhs and not (sep == '-' and rhs:match('/')) then if sep:find('%d') then return success, result -- to reject {{convert|1 234 567|m}} with a decent message (en only) end parms[i] = rhs table.insert(parms, i, sep) table.insert(parms, i, lhs) return extractor(i) end if not valstr:match('%-.*/') then for _, sep in ipairs(text_code.ranges.words) do local start, stop = valstr:find(sep, 2, true) -- start at 2 to skip any negative sign for range '-' if start then parms[i] = valstr:sub(stop + 1) table.insert(parms, i, sep) table.insert(parms, i, valstr:sub(1, start - 1)) return extractor(i) end end end end return success, result end local i = 1 local is_change while true do local success, info = extractor(i) -- need to set parms.opt_nocomma before calling this if not success then return false, info end i = i + 1 if is_change then info.is_change = true -- value is after "±" and so is a change (significant for range like {{convert|5|±|5|°C}}) is_change = nil end valinfo:add(info) local range_item = get_range(strip(parms[i])) if not range_item then break end i = i + 1 range:add(range_item) if type(range_item) == 'table' then -- For range "x", if append unit to some values, append it to all. parms.in_range_x = parms.in_range_x or range_item.in_range_x parms.out_range_x = parms.out_range_x or range_item.out_range_x parms.abbr_range_x = parms.abbr_range_x or range_item.abbr_range_x is_change = range_item.is_range_change end end if range.n > 0 then if range.n > 30 then -- limit abuse, although 4 is a more likely upper limit return false, { 'cvt_invalid_num' } -- misleading message but it will do end parms.range = range elseif had_nocomma then return false, { 'cvt_unknown', parm2 } end return true, valinfo, i end local function simple_get_values(parms) -- If input is like "{{convert|valid_value|valid_unit|...}}", -- return true, i, in_unit, in_unit_table -- i = index in parms of what follows valid_unit, if anything. -- The valid_value is not negative and does not use a fraction, and -- no options requiring further processing of the input are used. -- Otherwise, return nothing or return false, parm1 for caller to interpret. -- Testing shows this function is successful for 96% of converts in articles, -- and that on average it speeds up converts by 8%. local clean = to_en(strip(parms[1] or ''), parms) if parms.opt_ri or parms.opt_spell_in or #clean > 10 or not clean:match('^[0-9.]+$') then return false, clean end local value = tonumber(clean) if not value then return end local info = { value = value, altvalue = value, singular = (value == 1), clean = clean, show = with_separator(parms, clean), } local in_unit = strip(parms[2]) local success, in_unit_table = lookup(parms, in_unit, 'no_combination') if not success then return end in_unit_table.valinfo = { info } return true, 3, in_unit, in_unit_table end local function wikidata_call(parms, operation, ...) -- Return true, s where s is the result of a Wikidata operation, -- or return false, t where t is an error message table. local function worker(...) wikidata_code = wikidata_code or require(wikidata_module) wikidata_data = wikidata_data or mw.loadData(wikidata_data_module) return wikidata_code[operation](wikidata_data, ...) end local success, status, result = pcall(worker, ...) if success then return status, result end if parms.opt_sortable_debug then -- Use debug=yes to crash if an error while accessing Wikidata. error('Error accessing Wikidata: ' .. status, 0) end return false, { 'cvt_wd_fail' } end local function get_parms(parms, args) -- If successful, update parms and return true, unit where -- parms is a table of all arguments passed to the template -- converted to named arguments, and -- unit is the input unit table; -- or return false, t where t is an error message table. -- For special processing (not a convert), can also return -- true, wikitext where wikitext is the final result. -- The returned input unit table may be for a fake unit using the specified -- unit code as the symbol and name, and with bad_mcode = message code table. -- MediaWiki removes leading and trailing whitespace from the values of -- named arguments. However, the values of numbered arguments include any -- whitespace entered in the template, and whitespace is used by some -- parameters (example: the numbered parameters associated with "disp=x"). local kv_pairs = {} -- table of input key:value pairs where key is a name; needed because cannot iterate parms and add new fields to it for k, v in pairs(args) do if type(k) == 'number' or k == 'test' then -- parameter "test" is reserved for testing and is not translated parms[k] = v else kv_pairs[k] = v end end if parms.test == 'wikidata' then local ulookup = function (ucode) -- Use empty table for parms so it does not accumulate results when used repeatedly. return lookup({}, ucode, 'no_combination') end return wikidata_call(parms, '_listunits', ulookup) end local success, msg = translate_parms(parms, kv_pairs) if not success then return false, msg end if parms.input then success, msg = wikidata_call(parms, '_adjustparameters', parms, 1) if not success then return false, msg end end local success, i, in_unit, in_unit_table = simple_get_values(parms) if not success then if type(i) == 'string' and i:match('^NNN+$') then -- Some infoboxes have examples like {{convert|NNN|m}} (3 or more "N"). -- Output an empty string for these. return false, { 'cvt_no_output' } end local valinfo success, valinfo, i = get_values(parms) if not success then return false, valinfo end in_unit = strip(parms[i]) i = i + 1 success, in_unit_table = lookup(parms, in_unit, 'no_combination') if not success then in_unit = in_unit or '' if parms.opt_ignore_error then -- display given unit code with no error (for use with {{val}}) in_unit_table = '' -- suppress error message and prevent processing of output unit end in_unit_table = setmetatable({ symbol = in_unit, name2 = in_unit, utype = in_unit, scale = 1, default = '', defkey = '', linkey = '', bad_mcode = in_unit_table }, unit_mt) end in_unit_table.valinfo = valinfo end if parms.test == 'msg' then -- Am testing the messages produced when no output unit is specified, and -- the input unit has a missing or invalid default. -- Set two units for testing that. -- LATER: Remove this code. if in_unit == 'chain' then in_unit_table.default = nil -- no default elseif in_unit == 'rd' then in_unit_table.default = "ft!X!m" -- an invalid expression end end in_unit_table.inout = 'in' -- this is an input unit if not parms.range then local success, inext, composite_unit = get_composite(parms, i, in_unit_table) if not success then return false, inext end if composite_unit then in_unit_table = composite_unit i = inext end end if in_unit_table.builtin == 'mach' then -- As with old template, a number following Mach as the input unit is the altitude. -- That is deprecated: should use altitude_ft=NUMBER or altitude_m=NUMBER. local success, info success = tonumber(parms[i]) -- this will often work and will give correct result for values like 2e4 without forcing output scientific notation if success then info = { value = success } else success, info = extract_number(parms, parms[i], false, true) end if success then i = i + 1 in_unit_table.altitude = info.value end end local word = strip(parms[i]) i = i + 1 local precision, is_bad_precision local function set_precision(text) local number, is_integer = get_number(text) if number then if is_integer then precision = number else precision = text is_bad_precision = true end return true -- text was used for precision, good or bad end end if word and not set_precision(word) then parms.out_unit = parms.out_unit or word if set_precision(strip(parms[i])) then i = i + 1 end end if parms.opt_adj_mid then word = parms[i] i = i + 1 if word then -- mid-text words if word:sub(1, 1) == '-' then parms.mid = word else parms.mid = ' ' .. word end end end if parms.opt_one_preunit then parms[parms.opt_flip and 'preunit2' or 'preunit1'] = preunits(1, parms[i]) i = i + 1 end if parms.disp == 'x' then -- Following is reasonably compatible with the old template. local first = parms[i] or '' local second = parms[i+1] or '' i = i + 2 if strip(first) == '' then -- user can enter '&#32;' rather than ' ' to avoid the default first = ' [&nbsp;' .. first second = '&nbsp;]' .. second end parms.joins = { first, second } elseif parms.opt_two_preunits then local p1, p2 = preunits(2, parms[i], parms[i+1]) i = i + 2 if parms.preunit1 then -- To simplify documentation, allow unlikely use of adj=pre with disp=preunit -- (however, an output unit must be specified with adj=pre and with disp=preunit). parms.preunit1 = parms.preunit1 .. p1 parms.preunit2 = p2 else parms.preunit1, parms.preunit2 = p1, p2 end end if precision == nil then if set_precision(strip(parms[i])) then i = i + 1 end end if is_bad_precision then add_warning(parms, 1, 'cvt_bad_prec', precision) else parms.precision = precision end for j = i, i + 3 do local parm = parms[j] -- warn if find a non-empty extraneous parameter if parm and parm:match('%S') then add_warning(parms, 1, 'cvt_unknown_option', parm) break end end return true, in_unit_table end local function record_default_precision(parms, out_current, precision) -- If necessary, adjust parameters and return a possibly adjusted precision. -- When converting a range of values where a default precision is required, -- that default is calculated for each value because the result sometimes -- depends on the precise input and output values. This function may cause -- the entire convert process to be repeated in order to ensure that the -- same default precision is used for each individual convert. -- If that were not done, a range like 1000 to 1000.4 may give poor results -- because the first output could be heavily rounded, while the second is not. -- For range 1000.4 to 1000, this function can give the second convert the -- same default precision that was used for the first. if not parms.opt_round_each then local maxdef = out_current.max_default_precision if maxdef then if maxdef < precision then parms.do_convert_again = true out_current.max_default_precision = precision else precision = out_current.max_default_precision end else out_current.max_default_precision = precision end end return precision end local function default_precision(parms, invalue, inclean, denominator, outvalue, in_current, out_current, extra) -- Return a default value for precision (an integer like 2, 0, -2). -- If denominator is not nil, it is the value of the denominator in inclean. -- Code follows procedures used in old template. local fudge = 1e-14 -- {{Order of magnitude}} adds this, so we do too local prec, minprec, adjust local subunit_ignore_trailing_zero local subunit_more_precision -- kludge for "in" used in input like "|2|ft|6|in" local composite = in_current.composite if composite then subunit_ignore_trailing_zero = true -- input "|2|st|10|lb" has precision 0, not -1 if composite[#composite].exception == 'subunit_more_precision' then subunit_more_precision = true -- do not use standard precision with input like "|2|ft|6|in" end end if denominator and denominator > 0 then prec = math.max(log10(denominator), 1) else -- Count digits after decimal mark, handling cases like '12.345e6'. local exponent local integer, dot, decimals, expstr = inclean:match('^(%d*)(%.?)(%d*)(.*)') local e = expstr:sub(1, 1) if e == 'e' or e == 'E' then exponent = tonumber(expstr:sub(2)) end if dot == '' then prec = subunit_ignore_trailing_zero and 0 or -integer:match('0*$'):len() else prec = #decimals end if exponent then -- So '1230' and '1.23e3' both give prec = -1, and '0.00123' and '1.23e-3' give 5. prec = prec - exponent end end if in_current.istemperature and out_current.istemperature then -- Converting between common temperatures (°C, °F, °R, K); not keVT. -- Kelvin value can be almost zero, or small but negative due to precision problems. -- Also, an input value like -300 C (below absolute zero) gives negative kelvins. -- Calculate minimum precision from absolute value. adjust = 0 local kelvin = abs((invalue - in_current.offset) * in_current.scale) if kelvin < 1e-8 then -- assume nonzero due to input or calculation precision problem minprec = 2 else minprec = 2 - floor(log10(kelvin) + fudge) -- 3 sigfigs in kelvin end else if invalue == 0 or outvalue <= 0 then -- We are never called with a negative outvalue, but it might be zero. -- This is special-cased to avoid calculation exceptions. return record_default_precision(parms, out_current, 0) end if out_current.exception == 'integer_more_precision' and floor(invalue) == invalue then -- With certain output units that sometimes give poor results -- with default rounding, use more precision when the input -- value is equal to an integer. An example of a poor result -- is when input 50 gives a smaller output than input 49.5. -- Experiment shows this helps, but it does not eliminate all -- surprises because it is not clear whether "50" should be -- interpreted as "from 45 to 55" or "from 49.5 to 50.5". adjust = -log10(in_current.scale) elseif subunit_more_precision then -- Conversion like "{{convert|6|ft|1|in|cm}}" (where subunit is "in") -- has a non-standard adjust value, to give more output precision. adjust = log10(out_current.scale) + 2 else adjust = log10(abs(invalue / outvalue)) end adjust = adjust + log10(2) -- Ensure that the output has at least two significant figures. minprec = 1 - floor(log10(outvalue) + fudge) end if extra then adjust = extra.adjust or adjust minprec = extra.minprec or minprec end return record_default_precision(parms, out_current, math.max(floor(prec + adjust), minprec)) end local function convert(parms, invalue, info, in_current, out_current) -- Convert given input value from one unit to another. -- Return output_value (a number) if a simple convert, or -- return f, t where -- f = true, t = table of information with results, or -- f = false, t = error message table. local inscale = in_current.scale local outscale = out_current.scale if not in_current.iscomplex and not out_current.iscomplex then return invalue * (inscale / outscale) -- minimize overhead for most common case end if in_current.invert or out_current.invert then -- Inverted units, such as inverse length, inverse time, or -- fuel efficiency. Built-in units do not have invert set. if (in_current.invert or 1) * (out_current.invert or 1) < 0 then return 1 / (invalue * inscale * outscale) end return invalue * (inscale / outscale) elseif in_current.offset then -- Temperature (there are no built-ins for this type of unit). if info.is_change then return invalue * (inscale / outscale) end return (invalue - in_current.offset) * (inscale / outscale) + out_current.offset else -- Built-in unit. local in_builtin = in_current.builtin local out_builtin = out_current.builtin if in_builtin and out_builtin then if in_builtin == out_builtin then return invalue end -- There are no cases (yet) where need to convert from one -- built-in unit to another, so this should never occur. return false, { 'cvt_bug_convert' } end if in_builtin == 'mach' or out_builtin == 'mach' then -- Should check that only one altitude is given but am planning to remove -- in_current.altitude (which can only occur when Mach is the input unit), -- and out_current.altitude cannot occur. local alt = parms.altitude_ft or in_current.altitude if not alt and parms.altitude_m then alt = parms.altitude_m / 0.3048 -- 1 ft = 0.3048 m end local spd = speed_of_sound(alt) if in_builtin == 'mach' then inscale = spd return invalue * (inscale / outscale) end outscale = spd local adjust = 0.1 / inscale return true, { outvalue = invalue * (inscale / outscale), adjust = log10(adjust) + log10(2), } elseif in_builtin == 'hand' then -- 1 hand = 4 inches; 1.2 hands = 6 inches. -- Decimals of a hand are only defined for the first digit, and -- the first fractional digit should be a number of inches (1, 2 or 3). -- However, this code interprets the entire fractional part as the number -- of inches / 10 (so 1.75 inches would be 0.175 hands). -- A value like 12.3 hands is exactly 12*4 + 3 inches; base default precision on that. local integer, fracpart = math.modf(invalue) local inch_value = 4 * integer + 10 * fracpart -- equivalent number of inches local factor = inscale / outscale if factor == 4 then -- Am converting to inches: show exact result, and use "inches" not "in" by default. if parms.abbr_org == nil then out_current.usename = true end local show = format('%g', abs(inch_value)) -- show and clean are unsigned if not show:find('e', 1, true) then return true, { invalue = inch_value, outvalue = inch_value, clean = show, show = show, } end end local outvalue = (integer + 2.5 * fracpart) * factor local fracstr = info.clean:match('%.(.*)') or '' local fmt if fracstr == '' then fmt = '%.0f' else fmt = '%.' .. format('%d', #fracstr - 1) .. 'f' end return true, { invalue = inch_value, clean = format(fmt, inch_value), outvalue = outvalue, minprec = 0, } end end return false, { 'cvt_bug_convert' } -- should never occur end local function user_style(parms, i) -- Return text for a user-specified style for a table cell, or '' if none, -- given i = 1 (input style) or 2 (output style). local style = parms[(i == 1) and 'stylein' or 'styleout'] if style then style = style:gsub('"', '') if style ~= '' then if style:sub(-1) ~= ';' then style = style .. ';' end return style end end return '' end local function make_table_or_sort(parms, invalue, info, in_current, scaled_top) -- Set options to handle output for a table or a sort key, or both. -- The text sort key is based on the value resulting from converting -- the input to a fake base unit with scale = 1, and other properties -- required for a conversion derived from the input unit. -- For other modules, return the sort key in a hidden span element, and -- the scaled value used to generate the sort key. -- If scaled_top is set, it is the scaled value of the numerator of a per unit -- to be combined with this unit (the denominator) to make the sort key. -- Scaling only works with units that convert with a factor (not temperature). local sortkey, scaled_value if parms.opt_sortable_on then local base = { -- a fake unit with enough fields for a valid convert scale = 1, invert = in_current.invert and 1, iscomplex = in_current.iscomplex, offset = in_current.offset and 0, } local outvalue, extra = convert(parms, invalue, info, in_current, base) if extra then outvalue = extra.outvalue end if in_current.istemperature then -- Have converted to kelvin; assume numbers close to zero have a -- rounding error and should be zero. if abs(outvalue) < 1e-12 then outvalue = 0 end end if scaled_top and outvalue ~= 0 then outvalue = scaled_top / outvalue end scaled_value = outvalue if not valid_number(outvalue) then if outvalue < 0 then sortkey = '1000000000000000000' else sortkey = '9000000000000000000' end elseif outvalue == 0 then sortkey = '5000000000000000000' else local mag = floor(log10(abs(outvalue)) + 1e-14) local prefix if outvalue > 0 then prefix = 7000 + mag else prefix = 2999 - mag outvalue = outvalue + 10^(mag+1) end sortkey = format('%d', prefix) .. format('%015.0f', floor(outvalue * 10^(14-mag))) end end local sortspan if sortkey and not parms.table_align then sortspan = parms.opt_sortable_debug and '<span data-sort-value="' .. sortkey .. '♠"><span style="border:1px solid">' .. sortkey .. '♠</span></span>' or '<span data-sort-value="' .. sortkey .. '♠"></span>' parms.join_before = sortspan end if parms.table_align then local sort if sortkey then sort = ' data-sort-value="' .. sortkey .. '"' if parms.opt_sortable_debug then parms.join_before = '<span style="border:1px solid">' .. sortkey .. '</span>' end else sort = '' end local style = 'style="text-align:' .. parms.table_align .. ';' local joins = {} for i = 1, 2 do joins[i] = (i == 1 and '' or '\n|') .. style .. user_style(parms, i) .. '"' .. sort .. '|' end parms.table_joins = joins end return sortspan, scaled_value end local cvt_to_hand local function cvtround(parms, info, in_current, out_current) -- Return true, t where t is a table with the conversion results; fields: -- show = rounded, formatted string with the result of converting value in info, -- using the rounding specified in parms. -- singular = true if result (after rounding and ignoring any negative sign) -- is "1", or like "1.00", or is a fraction with value < 1; -- (and more fields shown below, and a calculated 'absvalue' field). -- or return false, t where t is an error message table. -- Input info.clean uses en digits (it has been translated, if necessary). -- Output show uses en or non-en digits as appropriate, or can be spelled. if out_current.builtin == 'hand' then return cvt_to_hand(parms, info, in_current, out_current) end local invalue = in_current.builtin == 'hand' and info.altvalue or info.value local outvalue, extra = convert(parms, invalue, info, in_current, out_current) if parms.need_table_or_sort then parms.need_table_or_sort = nil -- process using first input value only make_table_or_sort(parms, invalue, info, in_current) end if extra then if not outvalue then return false, extra end invalue = extra.invalue or invalue outvalue = extra.outvalue end if not valid_number(outvalue) then return false, { 'cvt_invalid_num' } end local isnegative if outvalue < 0 then isnegative = true outvalue = -outvalue end local precision, show, exponent local denominator = out_current.frac if denominator then show = fraction_table(outvalue, denominator) else precision = parms.precision if not precision then if parms.sigfig then show, exponent = make_sigfig(outvalue, parms.sigfig) elseif parms.opt_round then local n = parms.opt_round if n == 0.5 then local integer, fracpart = math.modf(floor(2 * outvalue + 0.5) / 2) if fracpart == 0 then show = format('%.0f', integer) else show = format('%.1f', integer + fracpart) end else show = format('%.0f', floor((outvalue / n) + 0.5) * n) end elseif in_current.builtin == 'mach' then local sigfig = info.clean:gsub('^[0.]+', ''):gsub('%.', ''):len() + 1 show, exponent = make_sigfig(outvalue, sigfig) else local inclean = info.clean if extra then inclean = extra.clean or inclean show = extra.show end if not show then precision = default_precision(parms, invalue, inclean, info.denominator, outvalue, in_current, out_current, extra) end end end end if precision then if precision >= 0 then local fudge if precision <= 8 then -- Add a fudge to handle common cases of bad rounding due to inability -- to precisely represent some values. This makes the following work: -- {{convert|-100.1|C|K}} and {{convert|5555000|um|m|2}}. -- Old template uses #expr round, which invokes PHP round(). -- LATER: Investigate how PHP round() works. fudge = 2e-14 else fudge = 0 end local fmt = '%.' .. format('%d', precision) .. 'f' local success success, show = pcall(format, fmt, outvalue + fudge) if not success then return false, { 'cvt_big_prec', tostring(precision) } end else precision = -precision -- #digits to zero (in addition to any digits after dot) local shift = 10 ^ precision show = format('%.0f', outvalue/shift) if show ~= '0' then exponent = #show + precision end end end local t = format_number(parms, show, exponent, isnegative) if type(show) == 'string' then -- Set singular using match because on some systems 0.99999999999999999 is 1.0. if exponent then t.singular = (exponent == 1 and show:match('^10*$')) else t.singular = (show == '1' or show:match('^1%.0*$')) end else t.fraction_table = show t.singular = (outvalue <= 1) -- cannot have 'fraction == 1', but if it were possible it would be singular end t.raw_absvalue = outvalue -- absolute value before rounding return true, setmetatable(t, { __index = function (self, key) if key == 'absvalue' then -- Calculate absolute value after rounding, if needed. local clean, exponent = rawget(self, 'clean'), rawget(self, 'exponent') local value = tonumber(clean) -- absolute value (any negative sign has been ignored) if exponent then value = value * 10^exponent end rawset(self, key, value) return value end end }) end function cvt_to_hand(parms, info, in_current, out_current) -- Convert input to hands, inches. -- Return true, t where t is a table with the conversion results; -- or return false, t where t is an error message table. if parms.abbr_org == nil then out_current.usename = true -- default is to show name not symbol end local precision = parms.precision local frac = out_current.frac if not frac and precision and precision > 1 then frac = (precision == 2) and 2 or 4 end local out_next = out_current.out_next if out_next then -- Use magic knowledge to determine whether the next unit is inches without requiring i18n. -- The following ensures that when the output combination "hand in" is used, the inches -- value is rounded to match the hands value. Also, displaying say "61½" instead of 61.5 -- is better as 61.5 implies the value is not 61.4. if out_next.exception == 'subunit_more_precision' then out_next.frac = frac end end -- Convert to inches; calculate hands from that. local dummy_unit_table = { scale = out_current.scale / 4, frac = frac } local success, outinfo = cvtround(parms, info, in_current, dummy_unit_table) if not success then return false, outinfo end local tfrac = outinfo.fraction_table local inches = outinfo.raw_absvalue if tfrac then inches = floor(inches) -- integer part only; fraction added later else inches = floor(inches + 0.5) -- a hands measurement never shows decimals of an inch end local hands, inches = divide(inches, 4) outinfo.absvalue = hands + inches/4 -- supposed to be the absolute rounded value, but this is close enough local inchstr = tostring(inches) -- '0', '1', '2' or '3' if precision and precision <= 0 then -- using negative or 0 for precision rounds to nearest hand hands = floor(outinfo.raw_absvalue/4 + 0.5) inchstr = '' elseif tfrac then -- Always show an integer before fraction (like "15.0½") because "15½" means 15-and-a-half hands. inchstr = numdot .. format_fraction(parms, 'out', false, inchstr, tfrac.numstr, tfrac.denstr) else inchstr = numdot .. from_en(inchstr) end outinfo.show = outinfo.sign .. with_separator(parms, format('%.0f', hands)) .. inchstr return true, outinfo end local function evaluate_condition(value, condition) -- Return true or false from applying a conditional expression to value, -- or throw an error if invalid. -- A very limited set of expressions is supported: -- v < 9 -- v * 9 < 9 -- where -- 'v' is replaced with value -- 9 is any number (as defined by Lua tonumber) -- only en digits are accepted -- '<' can also be '<=' or '>' or '>=' -- In addition, the following form is supported: -- LHS and RHS -- where -- LHS, RHS = any of above expressions. local function compare(value, text) local arithop, factor, compop, limit = text:match('^%s*v%s*([*]?)(.-)([<>]=?)(.*)$') if arithop == nil then error('Invalid default expression', 0) elseif arithop == '*' then factor = tonumber(factor) if factor == nil then error('Invalid default expression', 0) end value = value * factor end limit = tonumber(limit) if limit == nil then error('Invalid default expression', 0) end if compop == '<' then return value < limit elseif compop == '<=' then return value <= limit elseif compop == '>' then return value > limit elseif compop == '>=' then return value >= limit end error('Invalid default expression', 0) -- should not occur end local lhs, rhs = condition:match('^(.-%W)and(%W.*)') if lhs == nil then return compare(value, condition) end return compare(value, lhs) and compare(value, rhs) end local function get_default(value, unit_table) -- Return true, s where s = name of unit's default output unit, -- or return false, t where t is an error message table. -- Some units have a default that depends on the input value -- (the first value if a range of values is used). -- If '!' is in the default, the first bang-delimited field is an -- expression that uses 'v' to represent the input value. -- Example: 'v < 120 ! small ! big ! suffix' (suffix is optional) -- evaluates 'v < 120' as a boolean with result -- 'smallsuffix' if (value < 120), or 'bigsuffix' otherwise. -- Input must use en digits and '.' decimal mark. local default = data_code.default_exceptions[unit_table.defkey or unit_table.symbol] or unit_table.default if not default then local per = unit_table.per if per then local function a_default(v, u) local success, ucode = get_default(v, u) if not success then return '?' -- an unlikely error has occurred; will cause lookup of default to fail end -- Attempt to use only the first unit if a combination or output multiple. -- This is not bulletproof but should work for most cases. -- Where it does not work, the convert will need to specify the wanted output unit. local t = all_units[ucode] if t then local combo = t.combination if combo then -- For a multiple like ftin, the "first" unit (ft) is last in the combination. local i = t.multiple and table_len(combo) or 1 ucode = combo[i] end else -- Try for an automatically generated combination. local item = ucode:match('^(.-)%+') or ucode:match('^(%S+)%s') if all_units[item] then return item end end return ucode end local unit1, unit2 = per[1], per[2] local def1 = (unit1 and a_default(value, unit1) or unit_table.vprefix or '') local def2 = a_default(1, unit2) -- 1 because per unit of denominator return true, def1 .. '/' .. def2 end return false, { 'cvt_no_default', unit_table.symbol } end if default:find('!', 1, true) == nil then return true, default end local t = split(default, '!') if #t == 3 or #t == 4 then local success, result = pcall(evaluate_condition, value, t[1]) if success then default = result and t[2] or t[3] if #t == 4 then default = default .. t[4] end return true, default end end return false, { 'cvt_bad_default', unit_table.symbol } end local linked_pages -- to record linked pages so will not link to the same page more than once local function unlink(unit_table) -- Forget that the given unit has previously been linked (if it has). -- That is needed when processing a range of inputs or outputs when an id -- for the first range value may have been evaluated, but only an id for -- the last value is displayed, and that id may need to be linked. linked_pages[unit_table.unitcode or unit_table] = nil end local function make_link(link, id, unit_table) -- Return wikilink "[[link|id]]", possibly abbreviated as in examples: -- [[Mile|mile]] --> [[mile]] -- [[Mile|miles]] --> [[mile]]s -- However, just id is returned if: -- * no link given (so caller does not need to check if a link was defined); or -- * link has previously been used during the current convert (to avoid overlinking). local link_key if unit_table then link_key = unit_table.unitcode or unit_table else link_key = link end if not link or link == '' or linked_pages[link_key] then return id end linked_pages[link_key] = true -- Following only works for language en, but it should be safe on other wikis, -- and overhead of doing it generally does not seem worthwhile. local l = link:sub(1, 1):lower() .. link:sub(2) if link == id or l == id then return '[[' .. id .. ']]' elseif link .. 's' == id or l .. 's' == id then return '[[' .. id:sub(1, -2) .. ']]s' else return '[[' .. link .. '|' .. id .. ']]' end end local function variable_name(clean, unit_table) -- For slwiki, a unit name depends on the value. -- Parameter clean is the unsigned rounded value in en digits, as a string. -- Value Source Example for "m" -- integer 1: name1 meter (also is the name of the unit) -- integer 2: var{1} metra -- integer 3 and 4: var{2} metri -- integer else: var{3} metrov (0 and 5 or more) -- real/fraction: var{4} metra -- var{i} means the i'th field in unit_table.varname if it exists and has -- an i'th field, otherwise name2. -- Fields are separated with "!" and are not empty. -- A field for a unit using an SI prefix has the prefix name inserted, -- replacing '#' if found, or before the field otherwise. local vname if clean == '1' then vname = unit_table.name1 elseif unit_table.varname then local i if clean == '2' then i = 1 elseif clean == '3' or clean == '4' then i = 2 elseif clean:find('.', 1, true) then i = 4 else i = 3 end if i > 1 and varname == 'pl' then i = i - 1 end vname = split(unit_table.varname, '!')[i] end if vname then local si_name = rawget(unit_table, 'si_name') or '' local pos = vname:find('#', 1, true) if pos then vname = vname:sub(1, pos - 1) .. si_name .. vname:sub(pos + 1) else vname = si_name .. vname end return vname end return unit_table.name2 end local function linked_id(parms, unit_table, key_id, want_link, clean) -- Return final unit id (symbol or name), optionally with a wikilink, -- and update unit_table.sep if required. -- key_id is one of: 'symbol', 'sym_us', 'name1', 'name1_us', 'name2', 'name2_us'. local abbr_on = (key_id == 'symbol' or key_id == 'sym_us') if abbr_on and want_link then local symlink = rawget(unit_table, 'symlink') if symlink then return symlink -- for exceptions that have the linked symbol built-in end end local multiplier = rawget(unit_table, 'multiplier') local per = unit_table.per if per then local paren1, paren2 = '', '' -- possible parentheses around bottom unit local unit1 = per[1] -- top unit_table, or nil local unit2 = per[2] -- bottom unit_table if abbr_on then if not unit1 then unit_table.sep = '' -- no separator in "$2/acre" end if not want_link then local symbol = unit_table.symbol_raw if symbol then return symbol -- for exceptions that have the symbol built-in end end if (unit2.symbol):find('⋅', 1, true) then paren1, paren2 = '(', ')' end end local key_id2 -- unit2 is always singular if key_id == 'name2' then key_id2 = 'name1' elseif key_id == 'name2_us' then key_id2 = 'name1_us' else key_id2 = key_id end local result if abbr_on then result = '/' elseif omitsep then result = per_word elseif unit1 then result = ' ' .. per_word .. ' ' else result = per_word .. ' ' end if want_link and unit_table.link then if abbr_on or not varname then result = (unit1 and linked_id(parms, unit1, key_id, false, clean) or '') .. result .. linked_id(parms, unit2, key_id2, false, '1') else result = (unit1 and variable_name(clean, unit1) or '') .. result .. variable_name('1', unit2) end if omit_separator(result) then unit_table.sep = '' end return make_link(unit_table.link, result, unit_table) end if unit1 then result = linked_id(parms, unit1, key_id, want_link, clean) .. result if unit1.sep then unit_table.sep = unit1.sep end elseif omitsep then unit_table.sep = '' end return result .. paren1 .. linked_id(parms, unit2, key_id2, want_link, '1') .. paren2 end if multiplier then -- A multiplier (like "100" in "100km") forces the unit to be plural. multiplier = from_en(multiplier) if not omitsep then multiplier = multiplier .. (abbr_on and '&nbsp;' or ' ') end if not abbr_on then if key_id == 'name1' then key_id = 'name2' elseif key_id == 'name1_us' then key_id = 'name2_us' end end else multiplier = '' end local id = unit_table.fixed_name or ((varname and not abbr_on) and variable_name(clean, unit_table) or unit_table[key_id]) if omit_separator(id) then unit_table.sep = '' end if want_link then local link = data_code.link_exceptions[unit_table.linkey or unit_table.symbol] or unit_table.link if link then local before = '' local i = unit_table.customary if i == 1 and parms.opt_sp_us then i = 2 -- show "U.S." not "US" end if i == 3 and abbr_on then i = 4 -- abbreviate "imperial" to "imp" end local customary = text_code.customary_units[i] if customary then -- LATER: This works for language en only, but it's esoteric so ignore for now. local pertext if id:sub(1, 1) == '/' then -- Want unit "/USgal" to display as "/U.S. gal", not "U.S. /gal". pertext = '/' id = id:sub(2) elseif id:sub(1, 4) == 'per ' then -- Similarly want "per U.S. gallon", not "U.S. per gallon" (but in practice this is unlikely to be used). pertext = 'per ' id = id:sub(5) else pertext = '' end -- Omit any "US"/"U.S."/"imp"/"imperial" from start of id since that will be inserted. local removes = (i < 3) and { 'US&nbsp;', 'US ', 'U.S.&nbsp;', 'U.S. ' } or { 'imp&nbsp;', 'imp ', 'imperial ' } for _, prefix in ipairs(removes) do local plen = #prefix if id:sub(1, plen) == prefix then id = id:sub(plen + 1) break end end before = pertext .. make_link(customary.link, customary[1]) .. ' ' end id = before .. make_link(link, id, unit_table) end end return multiplier .. id end local function make_id(parms, which, unit_table) -- Return id, f where -- id = unit name or symbol, possibly modified -- f = true if id is a name, or false if id is a symbol -- using the value for index 'which', and for 'in' or 'out' (unit_table.inout). -- Result is '' if no symbol/name is to be used. -- In addition, set unit_table.sep = ' ' or '&nbsp;' or '' -- (the separator that caller will normally insert before the id). if parms.opt_values then unit_table.sep = '' return '' end local inout = unit_table.inout local info = unit_table.valinfo[which] local abbr_org = parms.abbr_org local adjectival = parms.opt_adjectival local lk = parms.lk local want_link = (lk == 'on' or lk == inout) local usename = unit_table.usename local singular = info.singular local want_name if usename then want_name = true else if abbr_org == nil then if parms.wantname then want_name = true end if unit_table.usesymbol then want_name = false end end if want_name == nil then local abbr = parms.abbr if abbr == 'on' or abbr == inout or (abbr == 'mos' and inout == 'out') then want_name = false else want_name = true end end end local key if want_name then if lk == nil and unit_table.builtin == 'hand' then want_link = true end if parms.opt_use_nbsp then unit_table.sep = '&nbsp;' else unit_table.sep = ' ' end if parms.opt_singular then local value if inout == 'in' then value = info.value else value = info.absvalue end if value then -- some unusual units do not always set value field value = abs(value) singular = (0 < value and value < 1.0001) end end if unit_table.engscale then -- engscale: so "|1|e3kg" gives "1 thousand kilograms" (plural) singular = false end key = (adjectival or singular) and 'name1' or 'name2' if parms.opt_sp_us then key = key .. '_us' end else if unit_table.builtin == 'hand' then if parms.opt_hand_hh then unit_table.symbol = 'hh' -- LATER: might want i18n applied to this end end unit_table.sep = '&nbsp;' key = parms.opt_sp_us and 'sym_us' or 'symbol' end return linked_id(parms, unit_table, key, want_link, info.clean), want_name end local function decorate_value(parms, unit_table, which, number_word) -- If needed, update unit_table so values will be shown with extra information. -- For consistency with the old template (but different from fmtpower), -- the style to display powers of 10 includes "display:none" to allow some -- browsers to copy, for example, "10³" as "10^3", rather than as "103". local info local engscale = unit_table.engscale local prefix = unit_table.vprefix if engscale or prefix then info = unit_table.valinfo[which] if info.decorated then return -- do not redecorate if repeating convert end info.decorated = true if engscale then local inout = unit_table.inout local abbr = parms.abbr if (abbr == 'on' or abbr == inout) and not parms.number_word then info.show = info.show .. '<span style="margin-left:0.2em">×<span style="margin-left:0.1em">' .. from_en('10') .. '</span></span><s style="display:none">^</s><sup>' .. from_en(tostring(engscale.exponent)) .. '</sup>' elseif number_word then local number_id local lk = parms.lk if lk == 'on' or lk == inout then number_id = make_link(engscale.link, engscale[1]) else number_id = engscale[1] end -- WP:NUMERAL recommends "&nbsp;" in values like "12 million". info.show = info.show .. (parms.opt_adjectival and '-' or '&nbsp;') .. number_id end end if prefix then info.show = prefix .. info.show end end end local function process_input(parms, in_current) -- Processing required once per conversion. -- Return block of text to represent input (value/unit). if parms.opt_output_only or parms.opt_output_number_only or parms.opt_output_unit_only then parms.joins = { '', '' } return '' end local first_unit local composite = in_current.composite -- nil or table of units if composite then first_unit = composite[1] else first_unit = in_current end local id1, want_name = make_id(parms, 1, first_unit) local sep = first_unit.sep -- separator between value and unit, set by make_id local preunit = parms.preunit1 if preunit then sep = '' -- any separator is included in preunit else preunit = '' end if parms.opt_input_unit_only then parms.joins = { '', '' } if composite then local parts = { id1 } for i, unit in ipairs(composite) do if i > 1 then table.insert(parts, (make_id(parms, 1, unit))) end end id1 = table.concat(parts, ' ') end if want_name and parms.opt_adjectival then return preunit .. hyphenated(id1) end return preunit .. id1 end if parms.opt_also_symbol and not composite and not parms.opt_flip then local join1 = parms.joins[1] if join1 == ' (' or join1 == ' [' then parms.joins = { ' [' .. first_unit[parms.opt_sp_us and 'sym_us' or 'symbol'] .. ']' .. join1 , parms.joins[2] } end end if in_current.builtin == 'mach' and first_unit.sep ~= '' then -- '' means omitsep with non-enwiki name local prefix = id1 .. '&nbsp;' local range = parms.range local valinfo = first_unit.valinfo local result = prefix .. valinfo[1].show if range then -- For simplicity and because more not needed, handle one range item only. local prefix2 = make_id(parms, 2, first_unit) .. '&nbsp;' result = range_text(range[1], want_name, parms, result, prefix2 .. valinfo[2].show, 'in', {spaced=true}) end return preunit .. result end if composite then -- Simplify: assume there is no range, and no decoration. local mid = (not parms.opt_flip) and parms.mid or '' local sep1 = '&nbsp;' local sep2 = ' ' if parms.opt_adjectival and want_name then sep1 = '-' sep2 = '-' end if omitsep and sep == '' then -- Testing the id of the most significant unit should be sufficient. sep1 = '' sep2 = '' end local parts = { first_unit.valinfo[1].show .. sep1 .. id1 } for i, unit in ipairs(composite) do if i > 1 then table.insert(parts, unit.valinfo[1].show .. sep1 .. (make_id(parms, 1, unit))) end end return table.concat(parts, sep2) .. mid end local add_unit = (parms.abbr == 'mos') or parms[parms.opt_flip and 'out_range_x' or 'in_range_x'] or (not want_name and parms.abbr_range_x) local range = parms.range if range and not add_unit then unlink(first_unit) end local id = range and make_id(parms, range.n + 1, first_unit) or id1 local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, 'in') if was_hyphenated then add_unit = false end local result local valinfo = first_unit.valinfo if range then for i = 0, range.n do local number_word if i == range.n then add_unit = false number_word = true end decorate_value(parms, first_unit, i+1, number_word) local show = valinfo[i+1].show if add_unit then show = show .. first_unit.sep .. (i == 0 and id1 or make_id(parms, i+1, first_unit)) end if i == 0 then result = show else result = range_text(range[i], want_name, parms, result, show, 'in') end end else decorate_value(parms, first_unit, 1, true) result = valinfo[1].show end return result .. preunit .. extra end local function process_one_output(parms, out_current) -- Processing required for each output unit. -- Return block of text to represent output (value/unit). local inout = out_current.inout -- normally 'out' but can be 'in' for order=out local id1, want_name = make_id(parms, 1, out_current) local sep = out_current.sep -- set by make_id local preunit = parms.preunit2 if preunit then sep = '' -- any separator is included in preunit else preunit = '' end if parms.opt_output_unit_only then if want_name and parms.opt_adjectival then return preunit .. hyphenated(id1) end return preunit .. id1 end if out_current.builtin == 'mach' and out_current.sep ~= '' then -- '' means omitsep with non-enwiki name local prefix = id1 .. '&nbsp;' local range = parms.range local valinfo = out_current.valinfo local result = prefix .. valinfo[1].show if range then -- For simplicity and because more not needed, handle one range item only. result = range_text(range[1], want_name, parms, result, prefix .. valinfo[2].show, inout, {spaced=true}) end return preunit .. result end local add_unit = (parms[parms.opt_flip and 'in_range_x' or 'out_range_x'] or (not want_name and parms.abbr_range_x)) and not parms.opt_output_number_only local range = parms.range if range and not add_unit then unlink(out_current) end local id = range and make_id(parms, range.n + 1, out_current) or id1 local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, inout) if was_hyphenated then add_unit = false end local result local valinfo = out_current.valinfo if range then for i = 0, range.n do local number_word if i == range.n then add_unit = false number_word = true end decorate_value(parms, out_current, i+1, number_word) local show = valinfo[i+1].show if add_unit then show = show .. out_current.sep .. (i == 0 and id1 or make_id(parms, i+1, out_current)) end if i == 0 then result = show else result = range_text(range[i], want_name, parms, result, show, inout) end end else decorate_value(parms, out_current, 1, true) result = valinfo[1].show end if parms.opt_output_number_only then return result end return result .. preunit .. extra end local function make_output_single(parms, in_unit_table, out_unit_table) -- Return true, item where item = wikitext of the conversion result -- for a single output (which is not a combination or a multiple); -- or return false, t where t is an error message table. if parms.opt_order_out and in_unit_table.unitcode == out_unit_table.unitcode then out_unit_table.valinfo = in_unit_table.valinfo else out_unit_table.valinfo = collection() for _, v in ipairs(in_unit_table.valinfo) do local success, info = cvtround(parms, v, in_unit_table, out_unit_table) if not success then return false, info end out_unit_table.valinfo:add(info) end end return true, process_one_output(parms, out_unit_table) end local function make_output_multiple(parms, in_unit_table, out_unit_table) -- Return true, item where item = wikitext of the conversion result -- for an output which is a multiple (like 'ftin'); -- or return false, t where t is an error message table. local inout = out_unit_table.inout -- normally 'out' but can be 'in' for order=out local multiple = out_unit_table.multiple -- table of scaling factors (will not be nil) local combos = out_unit_table.combination -- table of unit tables (will not be nil) local abbr = parms.abbr local abbr_org = parms.abbr_org local disp = parms.disp local want_name = (abbr_org == nil and (disp == 'or' or disp == 'slash')) or not (abbr == 'on' or abbr == inout or abbr == 'mos') local want_link = (parms.lk == 'on' or parms.lk == inout) local mid = parms.opt_flip and parms.mid or '' local sep1 = '&nbsp;' local sep2 = ' ' if parms.opt_adjectival and want_name then sep1 = '-' sep2 = '-' end local do_spell = parms.opt_spell_out parms.opt_spell_out = nil -- so the call to cvtround does not spell the value local function make_result(info, isfirst) local fmt, outvalue, sign local results = {} for i = 1, #combos do local tfrac, thisvalue, strforce local out_current = combos[i] out_current.inout = inout local scale = multiple[i] if i == 1 then -- least significant unit ('in' from 'ftin') local decimals out_current.frac = out_unit_table.frac local success, outinfo = cvtround(parms, info, in_unit_table, out_current) if not success then return false, outinfo end if isfirst then out_unit_table.valinfo = { outinfo } -- in case output value of first least significant unit is needed end sign = outinfo.sign tfrac = outinfo.fraction_table if outinfo.is_scientific then strforce = outinfo.show decimals = '' elseif tfrac then decimals = '' else local show = outinfo.show -- number as a string in local language local p1, p2 = show:find(numdot, 1, true) decimals = p1 and show:sub(p2 + 1) or '' -- text after numdot, if any end fmt = '%.' .. ulen(decimals) .. 'f' -- to reproduce precision if decimals == '' then if tfrac then outvalue = floor(outinfo.raw_absvalue) -- integer part only; fraction added later else outvalue = floor(outinfo.raw_absvalue + 0.5) -- keep all integer digits of least significant unit end else outvalue = outinfo.absvalue end end if scale then outvalue, thisvalue = divide(outvalue, scale) else thisvalue = outvalue end local id if want_name then if varname then local clean if strforce or tfrac then clean = '.1' -- dummy value to force name for floating point else clean = format(fmt, thisvalue) end id = variable_name(clean, out_current) else local key = 'name2' if parms.opt_adjectival then key = 'name1' elseif tfrac then if thisvalue == 0 then key = 'name1' end elseif parms.opt_singular then if 0 < thisvalue and thisvalue < 1.0001 then key = 'name1' end else if thisvalue == 1 then key = 'name1' end end id = out_current[key] end else id = out_current['symbol'] end if i == 1 and omit_separator(id) then -- Testing the id of the least significant unit should be sufficient. sep1 = '' sep2 = '' end if want_link then local link = out_current.link if link then id = make_link(link, id, out_current) end end local strval local spell_inout = (i == #combos or outvalue == 0) and inout or '' -- trick so the last value processed (first displayed) has uppercase, if requested if strforce and outvalue == 0 then sign = '' -- any sign is in strforce strval = strforce -- show small values in scientific notation; will only use least significant unit elseif tfrac then local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil strval = format_fraction(parms, spell_inout, false, wholestr, tfrac.numstr, tfrac.denstr, do_spell) else strval = (thisvalue == 0) and from_en('0') or with_separator(parms, format(fmt, thisvalue)) if do_spell then strval = spell_number(parms, spell_inout, strval) or strval end end table.insert(results, strval .. sep1 .. id) if outvalue == 0 then break end fmt = '%.0f' -- only least significant unit can have a non-integral value end local reversed, count = {}, #results for i = 1, count do reversed[i] = results[count + 1 - i] end return true, sign .. table.concat(reversed, sep2) end local valinfo = in_unit_table.valinfo local success, result = make_result(valinfo[1], true) if not success then return false, result end local range = parms.range if range then for i = 1, range.n do local success, result2 = make_result(valinfo[i+1]) if not success then return false, result2 end result = range_text(range[i], want_name, parms, result, result2, inout, {spaced=true}) end end return true, result .. mid end local function process(parms, in_unit_table, out_unit_table) -- Return true, s, outunit where s = final wikitext result, -- or return false, t where t is an error message table. linked_pages = {} local success, bad_output local bad_input_mcode = in_unit_table.bad_mcode -- nil if input unit is a valid convert unit local out_unit = parms.out_unit if out_unit == nil or out_unit == '' or type(out_unit) == 'function' then if bad_input_mcode or parms.opt_input_unit_only then bad_output = '' else local getdef = type(out_unit) == 'function' and out_unit or get_default success, out_unit = getdef(in_unit_table.valinfo[1].value, in_unit_table) parms.out_unit = out_unit if not success then bad_output = out_unit end end end if not bad_output and not out_unit_table then success, out_unit_table = lookup(parms, out_unit, 'any_combination') if success then local mismatch = check_mismatch(in_unit_table, out_unit_table) if mismatch then bad_output = mismatch end else bad_output = out_unit_table end end local lhs, rhs local flipped = parms.opt_flip and not bad_input_mcode if bad_output then rhs = (bad_output == '') and '' or message(parms, bad_output) elseif parms.opt_input_unit_only then rhs = '' else local combos -- nil (for 'ft' or 'ftin'), or table of unit tables (for 'm ft') if not out_unit_table.multiple then -- nil/false ('ft' or 'm ft'), or table of factors ('ftin') combos = out_unit_table.combination end local frac = parms.frac -- nil or denominator of fraction for output values if frac then -- Apply fraction to the unit (if only one), or to non-SI units (if a combination), -- except that if a precision is also specified, the fraction only applies to -- the hand unit; that allows the following result: -- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands) -- However, the following is handled elsewhere as a special case: -- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in) if combos then local precision = parms.precision for _, unit in ipairs(combos) do if unit.builtin == 'hand' or (not precision and not unit.prefixes) then unit.frac = frac end end else out_unit_table.frac = frac end end local outputs = {} local imax = combos and #combos or 1 -- 1 (single unit) or number of unit tables if imax == 1 then parms.opt_order_out = nil -- only useful with an output combination end if not flipped and not parms.opt_order_out then -- Process left side first so any duplicate links (from lk=on) are suppressed -- on right. Example: {{convert|28|e9pc|e9ly|abbr=off|lk=on}} lhs = process_input(parms, in_unit_table) end for i = 1, imax do local success, item local out_current = combos and combos[i] or out_unit_table out_current.inout = 'out' if i == 1 then if imax > 1 and out_current.builtin == 'hand' then out_current.out_next = combos[2] -- built-in hand can influence next unit in a combination end if parms.opt_order_out then out_current.inout = 'in' end end if out_current.multiple then success, item = make_output_multiple(parms, in_unit_table, out_current) else success, item = make_output_single(parms, in_unit_table, out_current) end if not success then return false, item end outputs[i] = item end if parms.opt_order_out then lhs = outputs[1] table.remove(outputs, 1) end local sep = parms.table_joins and parms.table_joins[2] or parms.join_between rhs = table.concat(outputs, sep) end if flipped or not lhs then local input = process_input(parms, in_unit_table) if flipped then lhs = rhs rhs = input else lhs = input end end if parms.join_before then lhs = parms.join_before .. lhs end local wikitext if bad_input_mcode then if bad_input_mcode == '' then wikitext = lhs else wikitext = lhs .. message(parms, bad_input_mcode) end elseif parms.table_joins then wikitext = parms.table_joins[1] .. lhs .. parms.table_joins[2] .. rhs else wikitext = lhs .. parms.joins[1] .. rhs .. parms.joins[2] end if parms.warnings and not bad_input_mcode then wikitext = wikitext .. parms.warnings end return true, get_styles(parms) .. wikitext, out_unit_table end local function main_convert(frame) -- Do convert, and if needed, do it again with higher default precision. local parms = { frame = frame } -- will hold template arguments, after translation set_config(frame.args) local success, result = get_parms(parms, frame:getParent().args) if success then if type(result) ~= 'table' then return tostring(result) end local in_unit_table = result local out_unit_table for _ = 1, 2 do -- use counter so cannot get stuck repeating convert success, result, out_unit_table = process(parms, in_unit_table, out_unit_table) if success and parms.do_convert_again then parms.do_convert_again = false else break end end end -- If input=x gives a problem, the result should be just the user input -- (if x is a property like P123 it has been replaced with ''). -- An unknown input unit would display the input and an error message -- with success == true at this point. -- Also, can have success == false with a message that outputs an empty string. if parms.input_text then if success and not parms.have_problem then return result end local cat if parms.tracking then -- Add a tracking category using the given text as the category sort key. -- There is currently only one type of tracking, but in principle multiple -- items could be tracked, using different sort keys for convenience. cat = wanted_category('tracking', parms.tracking) end return parms.input_text .. (cat or '') end return success and result or message(parms, result) end local function _unit(unitcode, options) -- Helper function for Module:Val to look up a unit. -- Parameter unitcode must be a string to identify the wanted unit. -- Parameter options must be nil or a table with optional fields: -- value = number (for sort key; default value is 1) -- scaled_top = nil for a normal unit, or a number for a unit which is -- the denominator of a per unit (for sort key) -- si = { 'symbol', 'link' } -- (a table with two strings) to make an SI unit -- that will be used for the look up -- link = true if result should be [[linked]] -- sort = 'on' or 'debug' if result should include a sort key in a -- span element ('debug' makes the key visible) -- name = true for the name of the unit instead of the symbol -- us = true for the US spelling of the unit, if any -- Return nil if unitcode is not a non-empty string. -- Otherwise return a table with fields: -- text = requested symbol or name of unit, optionally linked -- scaled_value = input value adjusted by unit scale; used for sort key -- sortspan = span element with sort key like that provided by {{ntsh}}, -- calculated from the result of converting value -- to a base unit with scale 1. -- unknown = true if the unitcode was not known unitcode = strip(unitcode) if unitcode == nil or unitcode == '' then return nil end set_config({}) linked_pages = {} options = options or {} local parms = { abbr = options.name and 'off' or 'on', lk = options.link and 'on' or nil, opt_sp_us = options.us and true or nil, opt_ignore_error = true, -- do not add pages using this function to 'what links here' for Module:Convert/extra opt_sortable_on = options.sort == 'on' or options.sort == 'debug', opt_sortable_debug = options.sort == 'debug', } if options.si then -- Make a dummy table of units (just one unit) for lookup to use. -- This makes lookup recognize any SI prefix in the unitcode. local symbol = options.si[1] or '?' parms.unittable = { [symbol] = { _name1 = symbol, _name2 = symbol, _symbol = symbol, utype = symbol, scale = symbol == 'g' and 0.001 or 1, prefixes = 1, default = symbol, link = options.si[2], }} end local success, unit_table = lookup(parms, unitcode, 'no_combination') if not success then unit_table = setmetatable({ symbol = unitcode, name2 = unitcode, utype = unitcode, scale = 1, default = '', defkey = '', linkey = '' }, unit_mt) end local value = tonumber(options.value) or 1 local clean = tostring(abs(value)) local info = { value = value, altvalue = value, singular = (clean == '1'), clean = clean, show = clean, } unit_table.inout = 'in' unit_table.valinfo = { info } local sortspan, scaled_value if options.sort then sortspan, scaled_value = make_table_or_sort(parms, value, info, unit_table, options.scaled_top) end return { text = make_id(parms, 1, unit_table), sortspan = sortspan, scaled_value = scaled_value, unknown = not success and true or nil, } end return { convert = main_convert, _unit = _unit } cac541bea61c5fbbcb0a2768343935e97587b60a Module:Convert/data 828 497 995 994 2023-07-10T16:51:30Z BigTa1k 2 1 revision imported Scribunto text/plain -- Conversion data used by [[Module:Convert]] which uses mw.loadData() for -- read-only access to this module so that it is loaded only once per page. -- See [[:en:Template:Convert/Transwiki guide]] if copying to another wiki. -- -- These data tables follow: -- all_units all properties for a unit, including default output -- default_exceptions exceptions for default output ('kg' and 'g' have different defaults) -- link_exceptions exceptions for links ('kg' and 'g' have different links) -- -- These tables are generated by a script which reads the wikitext of a page that -- documents the required properties of each unit; see [[:en:Module:Convert/doc]]. --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local all_units = { ["Gy"] = { _name1 = "gray", _symbol = "Gy", utype = "absorbed radiation dose", scale = 1, prefixes = 1, default = "rad", link = "Gray (unit)", }, ["rad"] = { _name1 = "rad", _symbol = "rad", utype = "absorbed radiation dose", scale = 0.01, prefixes = 1, default = "Gy", link = "Rad (unit)", }, ["cm/s2"] = { name1 = "centimetre per second squared", name1_us = "centimeter per second squared", name2 = "centimetres per second squared", name2_us = "centimeters per second squared", symbol = "cm/s<sup>2</sup>", utype = "acceleration", scale = 0.01, default = "ft/s2", link = "Gal (unit)", }, ["ft/s2"] = { name1 = "foot per second squared", name2 = "feet per second squared", symbol = "ft/s<sup>2</sup>", utype = "acceleration", scale = 0.3048, default = "m/s2", }, ["g0"] = { name1 = "standard gravity", name2 = "standard gravities", symbol = "''g''<sub>0</sub>", utype = "acceleration", scale = 9.80665, default = "m/s2", }, ["g-force"] = { name2 = "''g''", symbol = "''g''", utype = "acceleration", scale = 9.80665, default = "m/s2", link = "g-force", }, ["km/hs"] = { name1 = "kilometre per hour per second", name1_us = "kilometer per hour per second", name2 = "kilometres per hour per second", name2_us = "kilometers per hour per second", symbol = "km/(h⋅s)", utype = "acceleration", scale = 0.27777777777777779, default = "mph/s", link = "Acceleration", }, ["km/s2"] = { name1 = "kilometre per second squared", name1_us = "kilometer per second squared", name2 = "kilometres per second squared", name2_us = "kilometers per second squared", symbol = "km/s<sup>2</sup>", utype = "acceleration", scale = 1000, default = "mph/s", link = "Acceleration", }, ["m/s2"] = { name1 = "metre per second squared", name1_us = "meter per second squared", name2 = "metres per second squared", name2_us = "meters per second squared", symbol = "m/s<sup>2</sup>", utype = "acceleration", scale = 1, default = "ft/s2", }, ["mph/s"] = { name1 = "mile per hour per second", name2 = "miles per hour per second", symbol = "mph/s", utype = "acceleration", scale = 0.44704, default = "km/hs", link = "Acceleration", }, ["km/h/s"] = { target = "km/hs", }, ["standard gravity"] = { target = "g0", }, ["1000sqft"] = { name1 = "thousand square feet", name2 = "thousand square feet", symbol = "1000&nbsp;sq&nbsp;ft", utype = "area", scale = 92.90304, default = "m2", link = "Square foot", }, ["a"] = { _name1 = "are", _symbol = "a", utype = "area", scale = 100, prefixes = 1, default = "sqft", link = "Hectare#Are", }, ["acre"] = { symbol = "acre", usename = 1, utype = "area", scale = 4046.8564224, default = "ha", subdivs = { ["rood"] = { 4, default = "ha" }, ["sqperch"] = { 160, default = "ha" } }, }, ["acre-sing"] = { target = "acre", }, ["arpent"] = { symbol = "arpent", usename = 1, utype = "area", scale = 3418.89, default = "ha", }, ["cda"] = { name1 = "cuerda", symbol = "cda", utype = "area", scale = 3930.395625, default = "ha acre", }, ["daa"] = { name1 = "decare", symbol = "daa", utype = "area", scale = 1000, default = "km2 sqmi", }, ["dunam"] = { symbol = "dunam", usename = 1, utype = "area", scale = 1000, default = "km2 sqmi", }, ["dunum"] = { symbol = "dunum", usename = 1, utype = "area", scale = 1000, default = "km2 sqmi", link = "Dunam", }, ["ha"] = { name1 = "hectare", symbol = "ha", utype = "area", scale = 10000, default = "acre", }, ["hectare"] = { name1 = "hectare", symbol = "ha", usename = 1, utype = "area", scale = 10000, default = "acre", }, ["Irish acre"] = { name1 = "Irish acre", symbol = "Irish&nbsp;acres", utype = "area", scale = 6555.2385024, default = "ha", link = "Acre (Irish)", }, ["m2"] = { _name1 = "square metre", _name1_us= "square meter", _symbol = "m<sup>2</sup>", prefix_position= 8, utype = "area", scale = 1, prefixes = 2, default = "sqft", link = "Square metre", }, ["pondemaat"] = { name1 = "pondemaat", name2 = "pondemaat", symbol = "pond", utype = "area", scale = 3674.363358816, default = "m2", link = ":nl:pondemaat", }, ["pyeong"] = { name2 = "pyeong", symbol = "pyeong", usename = 1, utype = "area", scale = 3.3057851239669422, default = "m2", }, ["rai"] = { name2 = "rai", symbol = "rai", utype = "area", scale = 1600, default = "m2", link = "Rai (unit)", }, ["rood"] = { symbol = "rood", usename = 1, utype = "area", scale = 1011.7141056, default = "sqft m2", subdivs = { ["sqperch"] = { 40, default = "m2" } }, link = "Rood (unit)", }, ["sqfoot"] = { name1 = "square foot", name2 = "square foot", symbol = "sq&nbsp;ft", utype = "area", scale = 0.09290304, default = "m2", }, ["sqft"] = { name1 = "square foot", name2 = "square feet", symbol = "sq&nbsp;ft", utype = "area", scale = 0.09290304, default = "m2", }, ["sqin"] = { name1 = "square inch", name2 = "square inches", symbol = "sq&nbsp;in", utype = "area", scale = 0.00064516, default = "cm2", }, ["sqmi"] = { name1 = "square mile", symbol = "sq&nbsp;mi", utype = "area", scale = 2589988.110336, default = "km2", }, ["sqnmi"] = { name1 = "square nautical mile", symbol = "sq&nbsp;nmi", utype = "area", scale = 3429904, default = "km2 sqmi", link = "Nautical mile", }, ["sqperch"] = { name2 = "perches", symbol = "perch", usename = 1, utype = "area", scale = 25.29285264, default = "m2", link = "Rod (unit)#Area and volume", }, ["sqverst"] = { symbol = "square verst", usename = 1, utype = "area", scale = 1138062.24, default = "km2 sqmi", link = "Verst", }, ["sqyd"] = { name1 = "square yard", symbol = "sq&nbsp;yd", utype = "area", scale = 0.83612736, default = "m2", }, ["tsubo"] = { name2 = "tsubo", symbol = "tsubo", usename = 1, utype = "area", scale = 3.3057851239669422, default = "m2", link = "Japanese units of measurement#Area", }, ["acres"] = { target = "acre", }, ["are"] = { target = "a", }, ["decare"] = { target = "daa", }, ["foot2"] = { target = "sqfoot", }, ["ft2"] = { target = "sqft", }, ["in2"] = { target = "sqin", symbol = "in<sup>2</sup>", }, ["km²"] = { target = "km2", }, ["mi2"] = { target = "sqmi", }, ["million acre"] = { target = "e6acre", }, ["million acres"] = { target = "e6acre", }, ["million hectares"] = { target = "e6ha", }, ["m²"] = { target = "m2", }, ["nmi2"] = { target = "sqnmi", }, ["pond"] = { target = "pondemaat", }, ["sq arp"] = { target = "arpent", }, ["sqkm"] = { target = "km2", }, ["sqm"] = { target = "m2", }, ["square verst"] = { target = "sqverst", }, ["verst2"] = { target = "sqverst", }, ["yd2"] = { target = "sqyd", }, ["m2/ha"] = { name1 = "square metre per hectare", name1_us = "square meter per hectare", name2 = "square metres per hectare", name2_us = "square meters per hectare", symbol = "m<sup>2</sup>/ha", utype = "area per unit area", scale = 0.0001, default = "sqft/acre", link = "Basal area", }, ["sqft/acre"] = { name1 = "square foot per acre", name2 = "square feet per acre", symbol = "sq&nbsp;ft/acre", utype = "area per unit area", scale = 2.295684113865932e-5, default = "m2/ha", link = "Basal area", }, ["cent"] = { name1 = "cent", symbol = "¢", utype = "cent", scale = 1, default = "cent", link = "Cent (currency)", }, ["¢"] = { target = "cent", }, ["A.h"] = { name1 = "ampere hour", symbol = "A⋅h", utype = "charge", scale = 3600, default = "coulomb", }, ["coulomb"] = { _name1 = "coulomb", _symbol = "C", utype = "charge", scale = 1, prefixes = 1, default = "e", link = "Coulomb", }, ["e"] = { name1 = "elementary charge", symbol = "''e''", utype = "charge", scale = 1.602176487e-19, default = "coulomb", }, ["g-mol"] = { name1 = "gram-mole", symbol = "g&#8209;mol", utype = "chemical amount", scale = 1, default = "lbmol", link = "Mole (unit)", }, ["gmol"] = { name1 = "gram-mole", symbol = "gmol", utype = "chemical amount", scale = 1, default = "lbmol", link = "Mole (unit)", }, ["kmol"] = { name1 = "kilomole", symbol = "kmol", utype = "chemical amount", scale = 1000, default = "lbmol", link = "Mole (unit)", }, ["lb-mol"] = { name1 = "pound-mole", symbol = "lb&#8209;mol", utype = "chemical amount", scale = 453.59237, default = "mol", }, ["lbmol"] = { name1 = "pound-mole", symbol = "lbmol", utype = "chemical amount", scale = 453.59237, default = "mol", }, ["mol"] = { name1 = "mole", symbol = "mol", utype = "chemical amount", scale = 1, default = "lbmol", link = "Mole (unit)", }, ["kgCO2/L"] = { name1 = "kilogram per litre", name1_us = "kilogram per liter", name2 = "kilograms per litre", name2_us = "kilograms per liter", symbol = "kg(CO<sub>2</sub>)/L", utype = "co2 per unit volume", scale = 1000, default = "lbCO2/USgal", link = "Exhaust gas", }, ["lbCO2/USgal"] = { name1 = "pound per US gallon", name2 = "pounds per US gallon", symbol = "lbCO2/US&nbsp;gal", utype = "co2 per unit volume", scale = 119.82642731689663, default = "kgCO2/L", link = "Exhaust gas", }, ["oz/lb"] = { per = { "oz", "lb" }, utype = "concentration", default = "mg/kg", }, ["mg/kg"] = { per = { "mg", "kg" }, utype = "concentration", default = "oz/lb", }, ["g/dm3"] = { name1 = "gram per cubic decimetre", name1_us = "gram per cubic decimeter", name2 = "grams per cubic decimetre", name2_us = "grams per cubic decimeter", symbol = "g/dm<sup>3</sup>", utype = "density", scale = 1, default = "kg/m3", link = "Density", }, ["g/L"] = { name1 = "gram per litre", name1_us = "gram per liter", name2 = "grams per litre", name2_us = "grams per liter", symbol = "g/L", utype = "density", scale = 1, default = "lb/cuin", link = "Density", }, ["g/mL"] = { name1 = "gram per millilitre", name1_us = "gram per milliliter", name2 = "grams per millilitre", name2_us = "grams per milliliter", symbol = "g/mL", utype = "density", scale = 1000, default = "lb/cuin", link = "Density", }, ["g/ml"] = { name1 = "gram per millilitre", name1_us = "gram per milliliter", name2 = "grams per millilitre", name2_us = "grams per milliliter", symbol = "g/ml", utype = "density", scale = 1000, default = "lb/cuin", link = "Density", }, ["kg/dm3"] = { name1 = "kilogram per cubic decimetre", name1_us = "kilogram per cubic decimeter", name2 = "kilograms per cubic decimetre", name2_us = "kilograms per cubic decimeter", symbol = "kg/dm<sup>3</sup>", utype = "density", scale = 1000, default = "lb/cuft", link = "Density", }, ["kg/L"] = { name1 = "kilogram per litre", name1_us = "kilogram per liter", name2 = "kilograms per litre", name2_us = "kilograms per liter", symbol = "kg/L", utype = "density", scale = 1000, default = "lb/USgal", link = "Density", }, ["kg/l"] = { name1 = "kilogram per litre", name1_us = "kilogram per liter", name2 = "kilograms per litre", name2_us = "kilograms per liter", symbol = "kg/l", utype = "density", scale = 1000, default = "lb/USgal", link = "Density", }, ["kg/m3"] = { name1 = "kilogram per cubic metre", name1_us = "kilogram per cubic meter", name2 = "kilograms per cubic metre", name2_us = "kilograms per cubic meter", symbol = "kg/m<sup>3</sup>", utype = "density", scale = 1, default = "lb/cuyd", link = "Density", }, ["lb/cuft"] = { name1 = "pound per cubic foot", name2 = "pounds per cubic foot", symbol = "lb/cu&nbsp;ft", utype = "density", scale = 16.018463373960142, default = "g/cm3", link = "Density", }, ["lb/cuin"] = { name1 = "pound per cubic inch", name2 = "pounds per cubic inch", symbol = "lb/cu&nbsp;in", utype = "density", scale = 27679.904710203122, default = "g/cm3", link = "Density", }, ["lb/cuyd"] = { name1 = "pound per cubic yard", name2 = "pounds per cubic yard", symbol = "lb/cu&nbsp;yd", utype = "density", scale = 0.5932764212577829, default = "kg/m3", link = "Density", }, ["lb/impgal"] = { name1 = "pound per imperial gallon", name2 = "pounds per imperial gallon", symbol = "lb/imp&nbsp;gal", utype = "density", scale = 99.776372663101697, default = "kg/L", link = "Density", }, ["lb/in3"] = { name1 = "pound per cubic inch", name2 = "pounds per cubic inch", symbol = "lb/cu&thinsp;in", utype = "density", scale = 27679.904710203122, default = "g/cm3", link = "Density", }, ["lb/U.S.gal"] = { name1 = "pound per U.S. gallon", name2 = "pounds per U.S. gallon", symbol = "lb/U.S.&nbsp;gal", utype = "density", scale = 119.82642731689663, default = "kg/L", link = "Density", }, ["lb/USbu"] = { name1 = "pound per US bushel", name2 = "pounds per US bushel", symbol = "lb/US&nbsp;bu", utype = "density", scale = 12.871859780974471, default = "kg/m3", link = "Bushel", }, ["lb/USgal"] = { name1 = "pound per US gallon", name2 = "pounds per US gallon", symbol = "lb/US&nbsp;gal", utype = "density", scale = 119.82642731689663, default = "kg/L", link = "Density", }, ["lbm/cuin"] = { name1 = "pound mass per cubic inch", name2 = "pounds mass per cubic inch", symbol = "lbm/cu&thinsp;in", utype = "density", scale = 27679.904710203122, default = "g/cm3", link = "Density", }, ["mg/L"] = { name1 = "milligram per litre", name1_us = "milligram per liter", name2 = "milligrams per litre", name2_us = "milligrams per liter", symbol = "mg/L", utype = "density", scale = 0.001, default = "lb/cuin", link = "Density", }, ["oz/cuin"] = { name1 = "ounce per cubic inch", name2 = "ounces per cubic inch", symbol = "oz/cu&nbsp;in", utype = "density", scale = 1729.9940443876951, default = "g/cm3", link = "Density", }, ["g/cm3"] = { per = { "g", "cm3" }, utype = "density", default = "lb/cuin", }, ["g/m3"] = { per = { "g", "m3" }, utype = "density", default = "lb/cuyd", link = "Density", }, ["Mg/m3"] = { per = { "Mg", "m3" }, utype = "density", default = "lb/cuft", }, ["mg/l"] = { per = { "mg", "ll" }, utype = "density", default = "oz/cuin", }, ["μg/dL"] = { per = { "μg", "dL" }, utype = "density", default = "lb/cuin", }, ["μg/l"] = { per = { "μg", "ll" }, utype = "density", default = "oz/cuin", }, ["lb/ft3"] = { target = "lb/cuft", }, ["lb/yd3"] = { target = "lb/cuyd", }, ["lbm/in3"] = { target = "lbm/cuin", }, ["mcg/dL"] = { target = "μg/dL", }, ["oz/in3"] = { target = "oz/cuin", }, ["ug/dL"] = { target = "μg/dL", }, ["ug/l"] = { target = "μg/l", }, ["B.O.T.U."] = { name1 = "Board of Trade Unit", symbol = "B.O.T.U.", utype = "energy", scale = 3600000, default = "MJ", link = "Kilowatt-hour", }, ["bboe"] = { name1 = "barrel of oil equivalent", name2 = "barrels of oil equivalent", symbol = "bboe", utype = "energy", scale = 6117863200, default = "GJ", }, ["BOE"] = { name1 = "barrel of oil equivalent", name2 = "barrels of oil equivalent", symbol = "BOE", utype = "energy", scale = 6117863200, default = "GJ", }, ["BTU"] = { name1 = "British thermal unit", symbol = "BTU", utype = "energy", scale = 1055.05585262, default = "kJ", }, ["Btu"] = { name1 = "British thermal unit", symbol = "Btu", utype = "energy", scale = 1055.05585262, default = "kJ", }, ["BTU-39F"] = { name1 = "British thermal unit (39°F)", name2 = "British thermal units (39°F)", symbol = "BTU<sub>39°F</sub>", utype = "energy", scale = 1059.67, default = "kJ", link = "British thermal unit", }, ["Btu-39F"] = { name1 = "British thermal unit (39°F)", name2 = "British thermal units (39°F)", symbol = "Btu<sub>39°F</sub>", utype = "energy", scale = 1059.67, default = "kJ", link = "British thermal unit", }, ["BTU-59F"] = { name1 = "British thermal unit (59°F)", name2 = "British thermal units (59°F)", symbol = "BTU<sub>59°F</sub>", utype = "energy", scale = 1054.804, default = "kJ", link = "British thermal unit", }, ["Btu-59F"] = { name1 = "British thermal unit (59°F)", name2 = "British thermal units (59°F)", symbol = "Btu<sub>59°F</sub>", utype = "energy", scale = 1054.804, default = "kJ", link = "British thermal unit", }, ["BTU-60F"] = { name1 = "British thermal unit (60°F)", name2 = "British thermal units (60°F)", symbol = "BTU<sub>60°F</sub>", utype = "energy", scale = 1054.68, default = "kJ", link = "British thermal unit", }, ["Btu-60F"] = { name1 = "British thermal unit (60°F)", name2 = "British thermal units (60°F)", symbol = "Btu<sub>60°F</sub>", utype = "energy", scale = 1054.68, default = "kJ", link = "British thermal unit", }, ["BTU-63F"] = { name1 = "British thermal unit (63°F)", name2 = "British thermal units (63°F)", symbol = "BTU<sub>63°F</sub>", utype = "energy", scale = 1054.6, default = "kJ", link = "British thermal unit", }, ["Btu-63F"] = { name1 = "British thermal unit (63°F)", name2 = "British thermal units (63°F)", symbol = "Btu<sub>63°F</sub>", utype = "energy", scale = 1054.6, default = "kJ", link = "British thermal unit", }, ["BTU-ISO"] = { name1 = "British thermal unit (ISO)", name2 = "British thermal units (ISO)", symbol = "BTU<sub>ISO</sub>", utype = "energy", scale = 1055.056, default = "kJ", link = "British thermal unit", }, ["Btu-ISO"] = { target = "BTU-ISO", }, ["BTU-IT"] = { name1 = "British thermal unit (IT)", name2 = "British thermal units (IT)", symbol = "BTU<sub>IT</sub>", utype = "energy", scale = 1055.05585262, default = "kJ", link = "British thermal unit", }, ["Btu-IT"] = { name1 = "British thermal unit (IT)", name2 = "British thermal units (IT)", symbol = "Btu<sub>IT</sub>", utype = "energy", scale = 1055.05585262, default = "kJ", link = "British thermal unit", }, ["BTU-mean"] = { name1 = "British thermal unit (mean)", name2 = "British thermal units (mean)", symbol = "BTU<sub>mean</sub>", utype = "energy", scale = 1055.87, default = "kJ", link = "British thermal unit", }, ["Btu-mean"] = { name1 = "British thermal unit (mean)", name2 = "British thermal units (mean)", symbol = "Btu<sub>mean</sub>", utype = "energy", scale = 1055.87, default = "kJ", link = "British thermal unit", }, ["BTU-th"] = { name1 = "British thermal unit (thermochemical)", name2 = "British thermal units (thermochemical)", symbol = "BTU<sub>th</sub>", utype = "energy", scale = 1054.35026444, default = "kJ", link = "British thermal unit", }, ["Btu-th"] = { name1 = "British thermal unit (thermochemical)", name2 = "British thermal units (thermochemical)", symbol = "Btu<sub>th</sub>", utype = "energy", scale = 1054.35026444, default = "kJ", link = "British thermal unit", }, ["Cal"] = { name1 = "calorie", symbol = "Cal", utype = "energy", scale = 4184, default = "kJ", }, ["cal"] = { name1 = "calorie", symbol = "cal", utype = "energy", scale = 4.184, default = "J", }, ["Cal-15"] = { name1 = "Calorie (15°C)", name2 = "Calories (15°C)", symbol = "Cal<sub>15</sub>", utype = "energy", scale = 4185.8, default = "kJ", link = "Calorie", }, ["cal-15"] = { name1 = "calorie (15°C)", name2 = "calories (15°C)", symbol = "cal<sub>15</sub>", utype = "energy", scale = 4.1858, default = "J", link = "Calorie", }, ["Cal-IT"] = { name1 = "Calorie (International Steam Table)", name2 = "Calories (International Steam Table)", symbol = "Cal<sub>IT</sub>", utype = "energy", scale = 4186.8, default = "kJ", link = "Calorie", }, ["cal-IT"] = { name1 = "calorie (International Steam Table)", name2 = "calories (International Steam Table)", symbol = "cal<sub>IT</sub>", utype = "energy", scale = 4.1868, default = "J", link = "Calorie", }, ["Cal-th"] = { name1 = "Calorie (thermochemical)", name2 = "Calories (thermochemical)", symbol = "Cal<sub>th</sub>", utype = "energy", scale = 4184, default = "kJ", link = "Calorie", }, ["cal-th"] = { name1 = "calorie (thermochemical)", name2 = "calories (thermochemical)", symbol = "cal<sub>th</sub>", utype = "energy", scale = 4.184, default = "J", link = "Calorie", }, ["CHU-IT"] = { name1 = "Celsius heat unit (International Table)", name2 = "Celsius heat units (International Table)", symbol = "CHU<sub>IT</sub>", utype = "energy", scale = 1899.100534716, default = "kJ", link = "Conversion of units#Energy", }, ["cufootnaturalgas"] = { name1 = "cubic foot of natural gas", name2 = "cubic foot of natural gas", symbol = "cuftnaturalgas", usename = 1, utype = "energy", scale = 1055055.85262, default = "MJ", link = "Conversion of units#Energy", }, ["cuftnaturalgas"] = { name1 = "cubic foot of natural gas", name2 = "cubic feet of natural gas", symbol = "cuftnaturalgas", usename = 1, utype = "energy", scale = 1055055.85262, default = "MJ", link = "Conversion of units#Energy", }, ["Eh"] = { name1 = "Hartree", symbol = "''E''<sub>h</sub>", utype = "energy", scale = 4.35974417e-18, default = "eV", }, ["erg"] = { symbol = "erg", utype = "energy", scale = 0.0000001, default = "μJ", }, ["eV"] = { name1 = "electronvolt", symbol = "eV", utype = "energy", scale = 1.602176487e-19, default = "aJ", }, ["feV"] = { name1 = "femtoelectronvolt", symbol = "feV", utype = "energy", scale = 1.602176487e-34, default = "yJ", link = "Electronvolt", }, ["foe"] = { symbol = "foe", utype = "energy", scale = 1e44, default = "YJ", link = "Foe (unit)", }, ["ftlb"] = { name1 = "foot-pound", symbol = "ft⋅lb", utype = "energy", alttype = "torque", scale = 1.3558179483314004, default = "J", link = "Foot-pound (energy)", }, ["ftlb-f"] = { name1 = "foot-pound force", name2 = "foot-pounds force", symbol = "ft⋅lb<sub>f</sub>", utype = "energy", alttype = "torque", scale = 1.3558179483314004, default = "J", link = "Foot-pound (energy)", }, ["ftlbf"] = { name1 = "foot-pound force", name2 = "foot-pounds force", symbol = "ft⋅lbf", utype = "energy", alttype = "torque", scale = 1.3558179483314004, default = "J", link = "Foot-pound (energy)", }, ["ftpdl"] = { name1 = "foot-poundal", symbol = "ft⋅pdl", utype = "energy", scale = 0.0421401100938048, default = "J", }, ["GeV"] = { name1 = "gigaelectronvolt", symbol = "GeV", utype = "energy", scale = 1.602176487e-10, default = "nJ", link = "Electronvolt", }, ["gTNT"] = { name2 = "grams of TNT", symbol = "gram of TNT", usename = 1, utype = "energy", scale = 4184, default = "kJ", link = "TNT equivalent", }, ["Gtoe"] = { name1 = "gigatonne of oil equivalent", name2 = "gigatonnes of oil equivalent", symbol = "Gtoe", utype = "energy", scale = 4.1868e19, default = "EJ", link = "Tonne of oil equivalent", }, ["GtonTNT"] = { name2 = "gigatons of TNT", symbol = "gigaton of TNT", usename = 1, utype = "energy", scale = 4.184e18, default = "EJ", link = "TNT equivalent", }, ["GtTNT"] = { name2 = "gigatonnes of TNT", symbol = "gigatonne of TNT", usename = 1, utype = "energy", scale = 4.184e18, default = "EJ", link = "TNT equivalent", }, ["GW.h"] = { name1 = "gigawatt-hour", symbol = "GW⋅h", utype = "energy", scale = 3.6e12, default = "TJ", link = "Kilowatt-hour", }, ["GWh"] = { name1 = "gigawatt-hour", symbol = "GWh", utype = "energy", scale = 3.6e12, default = "TJ", link = "Kilowatt-hour", }, ["hph"] = { name1 = "horsepower-hour", symbol = "hp⋅h", utype = "energy", scale = 2684519.537696172792, default = "kWh", link = "Horsepower", }, ["inlb"] = { name1 = "inch-pound", symbol = "in⋅lb", utype = "energy", alttype = "torque", scale = 0.1129848290276167, default = "mJ", link = "Foot-pound (energy)", }, ["inlb-f"] = { name1 = "inch-pound force", name2 = "inch-pounds force", symbol = "in⋅lb<sub>f</sub>", utype = "energy", alttype = "torque", scale = 0.1129848290276167, default = "mJ", link = "Foot-pound (energy)", }, ["inlbf"] = { name1 = "inch-pound force", name2 = "inch-pounds force", symbol = "in⋅lbf", utype = "energy", alttype = "torque", scale = 0.1129848290276167, default = "mJ", link = "Foot-pound (energy)", }, ["inoz-f"] = { name1 = "inch-ounce force", name2 = "inch-ounces force", symbol = "in⋅oz<sub>f</sub>", utype = "energy", alttype = "torque", scale = 0.00706155181422604375, default = "mJ", link = "Foot-pound (energy)", }, ["inozf"] = { name1 = "inch-ounce force", name2 = "inch-ounces force", symbol = "in⋅ozf", utype = "energy", alttype = "torque", scale = 0.00706155181422604375, default = "mJ", link = "Foot-pound (energy)", }, ["J"] = { _name1 = "joule", _symbol = "J", utype = "energy", scale = 1, prefixes = 1, default = "cal", link = "Joule", }, ["kBOE"] = { name1 = "kilo barrel of oil equivalent", name2 = "kilo barrels of oil equivalent", symbol = "kBOE", utype = "energy", scale = 6.1178632e12, default = "TJ", link = "Barrel of oil equivalent", }, ["kcal"] = { name1 = "kilocalorie", symbol = "kcal", utype = "energy", scale = 4184, default = "kJ", link = "Calorie", }, ["kcal-15"] = { name1 = "kilocalorie (15°C)", name2 = "kilocalories (15°C)", symbol = "kcal<sub>15</sub>", utype = "energy", scale = 4185.8, default = "kJ", link = "Calorie", }, ["kcal-IT"] = { name1 = "kilocalorie (International Steam Table)", name2 = "kilocalories (International Steam Table)", symbol = "kcal<sub>IT</sub>", utype = "energy", scale = 4186.8, default = "kJ", link = "Calorie", }, ["kcal-th"] = { name1 = "kilocalorie (thermochemical)", name2 = "kilocalories (thermochemical)", symbol = "kcal<sub>th</sub>", utype = "energy", scale = 4184, default = "kJ", link = "Calorie", }, ["kerg"] = { name1 = "kiloerg", symbol = "kerg", utype = "energy", scale = 0.0001, default = "mJ", link = "Erg", }, ["keV"] = { name1 = "kiloelectronvolt", symbol = "keV", utype = "energy", scale = 1.602176487e-16, default = "fJ", link = "Electronvolt", }, ["kgTNT"] = { name2 = "kilograms of TNT", symbol = "kilogram of TNT", usename = 1, utype = "energy", scale = 4184000, default = "MJ", link = "TNT equivalent", }, ["kt(TNT)"] = { name1 = "kilotonne", name1_us = "kiloton", symbol = "kt", utype = "energy", scale = 4.184e12, default = "TJ", link = "TNT equivalent", }, ["ktoe"] = { name1 = "kilotonne of oil equivalent", name2 = "kilotonnes of oil equivalent", symbol = "ktoe", utype = "energy", scale = 4.1868e13, default = "TJ", link = "Tonne of oil equivalent", }, ["ktonTNT"] = { name1 = "kiloton of TNT", name2 = "kilotons of TNT", symbol = "kt", utype = "energy", scale = 4.184e12, default = "TJ", link = "TNT equivalent", }, ["ktTNT"] = { name2 = "kilotonnes of TNT", symbol = "kilotonne of TNT", usename = 1, utype = "energy", scale = 4.184e12, default = "TJ", link = "TNT equivalent", }, ["kW.h"] = { name1 = "kilowatt-hour", symbol = "kW⋅h", utype = "energy", scale = 3600000, default = "MJ", }, ["kWh"] = { name1 = "kilowatt-hour", symbol = "kWh", utype = "energy", scale = 3600000, default = "MJ", }, ["Mcal"] = { name1 = "megacalorie", symbol = "Mcal", utype = "energy", scale = 4184000, default = "MJ", link = "Calorie", }, ["mcal"] = { name1 = "millicalorie", symbol = "mcal", utype = "energy", scale = 0.004184, default = "mJ", link = "Calorie", }, ["Mcal-15"] = { name1 = "megacalorie (15°C)", name2 = "megacalories (15°C)", symbol = "Mcal<sub>15</sub>", utype = "energy", scale = 4185800, default = "MJ", link = "Calorie", }, ["mcal-15"] = { name1 = "millicalorie (15°C)", name2 = "millicalories (15°C)", symbol = "mcal<sub>15</sub>", utype = "energy", scale = 0.0041858, default = "mJ", link = "Calorie", }, ["Mcal-IT"] = { name1 = "megacalorie (International Steam Table)", name2 = "megacalories (International Steam Table)", symbol = "Mcal<sub>IT</sub>", utype = "energy", scale = 4186800, default = "MJ", link = "Calorie", }, ["mcal-IT"] = { name1 = "millicalorie (International Steam Table)", name2 = "millicalories (International Steam Table)", symbol = "mcal<sub>IT</sub>", utype = "energy", scale = 0.0041868, default = "mJ", link = "Calorie", }, ["Mcal-th"] = { name1 = "megacalorie (thermochemical)", name2 = "megacalories (thermochemical)", symbol = "Mcal<sub>th</sub>", utype = "energy", scale = 4184000, default = "MJ", link = "Calorie", }, ["mcal-th"] = { name1 = "millicalorie (thermochemical)", name2 = "millicalories (thermochemical)", symbol = "mcal<sub>th</sub>", utype = "energy", scale = 0.004184, default = "mJ", link = "Calorie", }, ["Merg"] = { name1 = "megaerg", symbol = "Merg", utype = "energy", scale = 0.1, default = "J", link = "Erg", }, ["merg"] = { name1 = "millierg", symbol = "merg", utype = "energy", scale = 0.0000000001, default = "μJ", link = "Erg", }, ["MeV"] = { name1 = "megaelectronvolt", symbol = "MeV", utype = "energy", scale = 1.602176487e-13, default = "pJ", link = "Electronvolt", }, ["meV"] = { name1 = "millielectronvolt", symbol = "meV", utype = "energy", scale = 1.602176487e-22, default = "zJ", link = "Electronvolt", }, ["MMBtu"] = { name1 = "million British thermal units", name2 = "million British thermal units", symbol = "MMBtu", utype = "energy", scale = 1055055852.62, default = "GJ", link = "British thermal unit", }, ["Mt(TNT)"] = { name1 = "megatonne", name1_us = "megaton", symbol = "Mt", utype = "energy", scale = 4.184e15, default = "PJ", link = "TNT equivalent", }, ["Mtoe"] = { name1 = "megatonne of oil equivalent", name2 = "megatonnes of oil equivalent", symbol = "Mtoe", utype = "energy", scale = 4.1868e16, default = "PJ", link = "Tonne of oil equivalent", }, ["MtonTNT"] = { name1 = "megaton of TNT", name2 = "megatons of TNT", symbol = "Mt", utype = "energy", scale = 4.184e15, default = "PJ", link = "TNT equivalent", }, ["mtonTNT"] = { name2 = "millitons of TNT", symbol = "milliton of TNT", usename = 1, utype = "energy", scale = 4184000, default = "MJ", link = "TNT equivalent", }, ["MtTNT"] = { name2 = "megatonnes of TNT", symbol = "megatonne of TNT", usename = 1, utype = "energy", scale = 4.184e15, default = "PJ", link = "TNT equivalent", }, ["mtTNT"] = { name2 = "millitonnes of TNT", symbol = "millitonne of TNT", usename = 1, utype = "energy", scale = 4184000, default = "MJ", link = "TNT equivalent", }, ["MW.h"] = { name1 = "megawatt-hour", symbol = "MW⋅h", utype = "energy", scale = 3600000000, default = "GJ", link = "Kilowatt-hour", }, ["mW.h"] = { name1 = "milliwatt-hour", symbol = "mW⋅h", utype = "energy", scale = 3.6, default = "J", link = "Kilowatt-hour", }, ["MWh"] = { name1 = "megawatt-hour", symbol = "MWh", utype = "energy", scale = 3600000000, default = "GJ", link = "Kilowatt-hour", }, ["mWh"] = { name1 = "milliwatt-hour", symbol = "mWh", utype = "energy", scale = 3.6, default = "J", link = "Kilowatt-hour", }, ["neV"] = { name1 = "nanoelectronvolt", symbol = "neV", utype = "energy", scale = 1.602176487e-28, default = "yJ", link = "Electronvolt", }, ["PeV"] = { name1 = "petaelectronvolt", symbol = "PeV", utype = "energy", scale = 0.0001602176487, default = "mJ", link = "Electronvolt", }, ["peV"] = { name1 = "picoelectronvolt", symbol = "peV", utype = "energy", scale = 1.602176487e-31, default = "yJ", link = "Electronvolt", }, ["PSh"] = { name1 = "Pferdestärkenstunde", symbol = "PSh", utype = "energy", scale = 2647795.5, default = "kWh", }, ["quad"] = { name1 = "quadrillion British thermal units", name2 = "quadrillion British thermal units", symbol = "quad", utype = "energy", scale = 1.054804e18, default = "EJ", link = "Quad (unit)", }, ["Ry"] = { name1 = "rydberg", symbol = "Ry", utype = "energy", scale = 2.1798741e-18, default = "eV", link = "Rydberg constant", }, ["scf"] = { name1 = "standard cubic foot", name2 = "standard cubic feet", symbol = "scf", utype = "energy", scale = 2869.2044809344, default = "kJ", }, ["scfoot"] = { name1 = "standard cubic foot", name2 = "standard cubic foot", symbol = "scf", utype = "energy", scale = 2869.2044809344, default = "kJ", }, ["t(TNT)"] = { name1 = "tonne", name1_us = "ton", symbol = "t", utype = "energy", scale = 4184000000, default = "GJ", link = "TNT equivalent", }, ["TeV"] = { name1 = "teraelectronvolt", symbol = "TeV", utype = "energy", scale = 1.602176487e-7, default = "μJ", link = "Electronvolt", }, ["th"] = { name1 = "thermie", symbol = "th", utype = "energy", scale = 4186800, default = "MJ", link = "Conversion of units#Energy", }, ["thm-EC"] = { name1 = "therm (EC)", name2 = "therms (EC)", symbol = "thm (EC)", utype = "energy", scale = 105506000, default = "MJ", link = "Therm", }, ["thm-UK"] = { name1 = "therm (UK)", name2 = "therms (UK)", symbol = "thm (UK)", utype = "energy", scale = 105505585.257348, default = "MJ", link = "Therm", }, ["thm-US"] = { name1 = "therm (US)", name1_us = "therm (U.S.)", name2 = "therms (US)", name2_us = "therms (U.S.)", symbol = "thm (US)", sym_us = "thm (U.S.)", utype = "energy", scale = 105480400, default = "MJ", link = "Therm", }, ["toe"] = { name1 = "tonne of oil equivalent", name2 = "tonnes of oil equivalent", symbol = "toe", utype = "energy", scale = 41868000000, default = "GJ", }, ["tonTNT"] = { name2 = "tons of TNT", symbol = "ton of TNT", usename = 1, utype = "energy", scale = 4184000000, default = "GJ", link = "TNT equivalent", }, ["tTNT"] = { name2 = "tonnes of TNT", symbol = "tonne of TNT", usename = 1, utype = "energy", scale = 4184000000, default = "GJ", link = "TNT equivalent", }, ["TtonTNT"] = { name2 = "teratons of TNT", symbol = "teraton of TNT", usename = 1, utype = "energy", scale = 4.184e21, default = "ZJ", link = "TNT equivalent", }, ["TtTNT"] = { name2 = "teratonnes of TNT", symbol = "teratonne of TNT", usename = 1, utype = "energy", scale = 4.184e21, default = "ZJ", link = "TNT equivalent", }, ["TW.h"] = { name1 = "terawatt-hour", symbol = "TW⋅h", utype = "energy", scale = 3.6e15, default = "PJ", link = "Kilowatt-hour", }, ["TWh"] = { name1 = "terawatt-hour", symbol = "TWh", utype = "energy", scale = 3.6e15, default = "PJ", link = "Kilowatt-hour", }, ["W.h"] = { name1 = "watt-hour", symbol = "W⋅h", utype = "energy", scale = 3600, default = "kJ", link = "Kilowatt-hour", }, ["Wh"] = { name1 = "watt-hour", symbol = "Wh", utype = "energy", scale = 3600, default = "kJ", link = "Kilowatt-hour", }, ["μerg"] = { name1 = "microerg", symbol = "μerg", utype = "energy", scale = 1e-13, default = "nJ", link = "Erg", }, ["μeV"] = { name1 = "microelectronvolt", symbol = "μeV", utype = "energy", scale = 1.602176487e-25, default = "yJ", link = "Electronvolt", }, ["μW.h"] = { name1 = "microwatt-hour", symbol = "μW⋅h", utype = "energy", scale = 0.0036, default = "mJ", link = "Kilowatt-hour", }, ["μWh"] = { name1 = "microwatt-hour", symbol = "μWh", utype = "energy", scale = 0.0036, default = "mJ", link = "Kilowatt-hour", }, ["-kW.h"] = { target = "kW.h", link = "Kilowatt hour", }, ["btu"] = { target = "BTU", }, ["Calorie"] = { target = "Cal", }, ["ft.lbf"] = { target = "ftlbf", }, ["ft·lbf"] = { target = "ftlbf", }, ["g-cal-15"] = { target = "cal-15", }, ["g-cal-IT"] = { target = "cal-IT", }, ["g-cal-th"] = { target = "cal-th", }, ["g-kcal-15"] = { target = "kcal-15", }, ["g-kcal-IT"] = { target = "kcal-IT", }, ["g-kcal-th"] = { target = "kcal-th", }, ["g-Mcal-15"] = { target = "Mcal-15", }, ["g-mcal-15"] = { target = "mcal-15", }, ["g-Mcal-IT"] = { target = "Mcal-IT", }, ["g-mcal-IT"] = { target = "mcal-IT", }, ["g-Mcal-th"] = { target = "Mcal-th", }, ["g-mcal-th"] = { target = "mcal-th", }, ["GW-h"] = { target = "GW.h", }, ["GW·h"] = { target = "GW.h", }, ["Hartree"] = { target = "Eh", }, ["hp.h"] = { target = "hph", }, ["in.lb-f"] = { target = "inlb-f", }, ["in.lbf"] = { target = "inlbf", }, ["in.oz-f"] = { target = "inoz-f", }, ["in.ozf"] = { target = "inozf", }, ["kbboe"] = { target = "kBOE", symbol = "kbboe", }, ["kg-cal-15"] = { target = "Cal-15", }, ["kg-cal-IT"] = { target = "Cal-IT", }, ["kg-cal-th"] = { target = "Cal-th", }, ["kW-h"] = { target = "kW.h", }, ["kW·h"] = { target = "kW.h", }, ["MW-h"] = { target = "MW.h", }, ["mW-h"] = { target = "mW.h", }, ["MW·h"] = { target = "MW.h", }, ["TW-h"] = { target = "TW.h", }, ["uerg"] = { target = "μerg", }, ["ueV"] = { target = "μeV", }, ["uW-h"] = { target = "μW.h", }, ["uW.h"] = { target = "μW.h", }, ["uWh"] = { target = "μWh", }, ["W-h"] = { target = "W.h", }, ["eVpar"] = { _name1 = "electronvolt", _symbol = "eV", utype = "energy per chemical amount", scale = 96485.329522144166, prefixes = 1, default = "kcal/mol", link = "Electronvolt", }, ["kcal/mol"] = { per = { "kcal", "mol" }, utype = "energy per chemical amount", default = "kJ/mol", link = "Kilocalorie per mole", }, ["kJ/mol"] = { per = { "kJ", "mol" }, utype = "energy per chemical amount", default = "kcal/mol", link = "Joule per mole", }, ["kWh/100 km"] = { name1 = "kilowatt-hour per 100 kilometres", name1_us = "kilowatt-hour per 100 kilometers", name2 = "kilowatt-hours per 100 kilometres", name2_us = "kilowatt-hours per 100 kilometers", symbol = "kW⋅h/100&nbsp;km", utype = "energy per unit length", scale = 36, default = "MJ/km kWh/mi", link = "Kilowatt-hour", }, ["kWh/100 mi"] = { name1 = "kilowatt-hour per 100 miles", name2 = "kilowatt-hours per 100 miles", symbol = "kW⋅h/100&nbsp;mi", utype = "energy per unit length", scale = 22.3694, default = "mpge", link = "Miles per gallon gasoline equivalent", }, ["MJ/100 km"] = { name1 = "megajoule per 100 kilometres", name1_us = "megajoule per 100 kilometers", name2 = "megajoules per 100 kilometres", name2_us = "megajoules per 100 kilometers", symbol = "MJ/100&nbsp;km", utype = "energy per unit length", scale = 10, default = "BTU/mi", link = "British thermal unit", }, ["mpge"] = { name1 = "mile per gallon gasoline equivalent", name2 = "miles per gallon gasoline equivalent", symbol = "mpg&#8209;e", utype = "energy per unit length", scale = 1.3263314048360777e-5, invert = -1, iscomplex= true, default = "kWh/100 mi", link = "Miles per gallon gasoline equivalent", }, ["BTU/mi"] = { per = { "BTU", "mi" }, utype = "energy per unit length", default = "v > 1525 ! M ! k ! J/km", }, ["kJ/km"] = { per = { "kJ", "km" }, utype = "energy per unit length", default = "BTU/mi", }, ["kWh/km"] = { per = { "-kW.h", "km" }, utype = "energy per unit length", default = "MJ/km kWh/mi", }, ["kWh/mi"] = { per = { "-kW.h", "mi" }, utype = "energy per unit length", default = "kWh/km MJ/km", }, ["MJ/km"] = { per = { "MJ", "km" }, utype = "energy per unit length", default = "BTU/mi", }, ["mpg-e"] = { target = "mpge", }, ["BTU/lb"] = { name1 = "British thermal unit per pound", name2 = "British thermal units per pound", symbol = "BTU/lb", utype = "energy per unit mass", scale = 2326, default = "kJ/kg", link = "British thermal unit", }, ["cal/g"] = { name1 = "calorie per gram", name2 = "calories per gram", symbol = "cal/g", utype = "energy per unit mass", scale = 4184, default = "J/g", }, ["GJ/kg"] = { name1 = "gigajoule per kilogram", name2 = "gigajoules per kilogram", symbol = "GJ/kg", utype = "energy per unit mass", scale = 1e9, default = "ktTNT/t", link = "Specific energy", }, ["J/g"] = { name1 = "joule per gram", name2 = "joules per gram", symbol = "J/g", utype = "energy per unit mass", scale = 1000, default = "kcal/g", link = "Specific energy", }, ["kcal/g"] = { name1 = "kilocalorie per gram", name2 = "kilocalories per gram", symbol = "kcal/g", utype = "energy per unit mass", scale = 4184000, default = "kJ/g", }, ["kJ/g"] = { name1 = "kilojoule per gram", name2 = "kilojoules per gram", symbol = "kJ/g", utype = "energy per unit mass", scale = 1000000, default = "kcal/g", link = "Specific energy", }, ["kJ/kg"] = { name1 = "kilojoule per kilogram", name2 = "kilojoules per kilogram", symbol = "kJ/kg", utype = "energy per unit mass", scale = 1000, default = "BTU/lb", link = "Specific energy", }, ["ktonTNT/MT"] = { name2 = "kilotons of TNT per metric ton", symbol = "kiloton of TNT per metric ton", usename = 1, utype = "energy per unit mass", scale = 4184000000, default = "GJ/kg", link = "TNT equivalent", }, ["ktTNT/t"] = { name2 = "kilotonnes of TNT per tonne", symbol = "kilotonne of TNT per tonne", usename = 1, utype = "energy per unit mass", scale = 4184000000, default = "GJ/kg", link = "TNT equivalent", }, ["MtonTNT/MT"] = { name2 = "megatons of TNT per metric ton", symbol = "megaton of TNT per metric ton", usename = 1, utype = "energy per unit mass", scale = 4.184e12, default = "TJ/kg", link = "TNT equivalent", }, ["MtTNT/MT"] = { name2 = "megatonnes of TNT per tonne", symbol = "megatonne of TNT per tonne", usename = 1, utype = "energy per unit mass", scale = 4.184e12, default = "TJ/kg", link = "TNT equivalent", }, ["TJ/kg"] = { name1 = "terajoule per kilogram", name2 = "terajoules per kilogram", symbol = "TJ/kg", utype = "energy per unit mass", scale = 1e12, default = "MtTNT/MT", link = "Specific energy", }, ["Cal/g"] = { per = { "Cal", "g" }, utype = "energy per unit mass", default = "kJ/g", }, ["BTU/cuft"] = { per = { "BTU", "cuft" }, utype = "energy per unit volume", default = "kJ/L", }, ["Cal/12USoz(mL)serve"] = { per = { "Cal", "-12USoz(mL)serve" }, utype = "energy per unit volume", default = "kJ/L", }, ["Cal/12USoz(ml)serve"] = { per = { "Cal", "-12USoz(ml)serve" }, utype = "energy per unit volume", default = "kJ/l", }, ["Cal/12USozserve"] = { per = { "Cal", "-12USozserve" }, utype = "energy per unit volume", default = "kJ/L", }, ["Cal/USoz"] = { per = { "Cal", "USoz" }, utype = "energy per unit volume", default = "kJ/ml", }, ["kJ/L"] = { per = { "kJ", "L" }, utype = "energy per unit volume", default = "BTU/cuft", }, ["kJ/l"] = { per = { "kJ", "ll" }, utype = "energy per unit volume", default = "BTU/cuft", }, ["kJ/ml"] = { per = { "kJ", "ml" }, utype = "energy per unit volume", default = "Cal/USoz", }, ["MJ/m3"] = { per = { "MJ", "m3" }, utype = "energy per unit volume", default = "BTU/cuft", }, ["Sv"] = { _name1 = "sievert", _symbol = "Sv", utype = "equivalent radiation dose", scale = 1, prefixes = 1, default = "rem", link = "Sievert", }, ["rem"] = { _name1 = "rem", _symbol = "rem", utype = "equivalent radiation dose", scale = 0.01, prefixes = 1, default = "Sv", link = "Roentgen equivalent man", }, ["g/km"] = { name1 = "gram per kilometre", name1_us = "gram per kilometer", name2 = "grams per kilometre", name2_us = "grams per kilometer", symbol = "g/km", utype = "exhaust emission", scale = 1e-6, default = "oz/mi", link = "Exhaust gas", }, ["g/mi"] = { name1 = "gram per mile", name2 = "grams per mile", symbol = "g/mi", utype = "exhaust emission", scale = 6.2137119223733397e-7, default = "g/km", link = "Exhaust gas", }, ["gCO2/km"] = { name1 = "gram of CO<sub>2</sub> per kilometre", name1_us = "gram of CO<sub>2</sub> per kilometer", name2 = "grams of CO<sub>2</sub> per kilometre", name2_us = "grams of CO<sub>2</sub> per kilometer", symbol = "g(CO<sub>2</sub>)/km", utype = "exhaust emission", scale = 1e-6, default = "ozCO2/mi", link = "Exhaust gas", }, ["gCO2/mi"] = { name1 = "gram of CO<sub>2</sub> per mile", name2 = "grams of CO<sub>2</sub> per mile", symbol = "g(CO<sub>2</sub>)/mi", utype = "exhaust emission", scale = 6.2137119223733397e-7, default = "gCO2/km", link = "Exhaust gas", }, ["kg/km"] = { name1 = "kilogram per kilometre", name1_us = "kilogram per kilometer", name2 = "kilograms per kilometre", name2_us = "kilograms per kilometer", symbol = "kg/km", utype = "exhaust emission", scale = 0.001, default = "lb/mi", link = "Exhaust gas", }, ["kgCO2/km"] = { name1 = "kilogram of CO<sub>2</sub> per kilometre", name1_us = "kilogram of CO<sub>2</sub> per kilometer", name2 = "kilograms of CO<sub>2</sub> per kilometre", name2_us = "kilograms of CO<sub>2</sub> per kilometer", symbol = "kg(CO<sub>2</sub>)/km", utype = "exhaust emission", scale = 0.001, default = "lbCO2/mi", link = "Exhaust gas", }, ["lb/mi"] = { name1 = "pound per mile", name2 = "pounds per mile", symbol = "lb/mi", utype = "exhaust emission", scale = 0.00028184923173665794, default = "kg/km", link = "Exhaust gas", }, ["lbCO2/mi"] = { name1 = "pound of CO<sub>2</sub> per mile", name2 = "pounds of CO<sub>2</sub> per mile", symbol = "lb(CO<sub>2</sub>)/mi", utype = "exhaust emission", scale = 0.00028184923173665794, default = "kgCO2/km", link = "Exhaust gas", }, ["oz/mi"] = { name1 = "ounce per mile", name2 = "ounces per mile", symbol = "oz/mi", utype = "exhaust emission", scale = 1.7615576983541121e-5, default = "g/km", link = "Exhaust gas", }, ["ozCO2/mi"] = { name1 = "ounce of CO<sub>2</sub> per mile", name2 = "ounces of CO<sub>2</sub> per mile", symbol = "oz(CO<sub>2</sub>)/mi", utype = "exhaust emission", scale = 1.7615576983541121e-5, default = "gCO2/km", link = "Exhaust gas", }, ["cuft/a"] = { name1 = "cubic foot per annum", name2 = "cubic feet per annum", symbol = "cu&nbsp;ft/a", utype = "flow", scale = 8.9730672142368242e-10, default = "m3/a", link = "Cubic foot per second", }, ["cuft/d"] = { name1 = "cubic foot per day", name2 = "cubic feet per day", symbol = "cu&nbsp;ft/d", utype = "flow", scale = 3.2774128000000003e-7, default = "m3/d", link = "Cubic foot per second", }, ["cuft/h"] = { name1 = "cubic foot per hour", name2 = "cubic feet per hour", symbol = "cu&nbsp;ft/h", utype = "flow", scale = 7.8657907200000004e-6, default = "m3/h", link = "Cubic foot per second", }, ["cuft/min"] = { name1 = "cubic foot per minute", name2 = "cubic feet per minute", symbol = "cu&nbsp;ft/min", utype = "flow", scale = 0.00047194744319999999, default = "m3/min", }, ["cuft/s"] = { name1 = "cubic foot per second", name2 = "cubic feet per second", symbol = "cu&nbsp;ft/s", utype = "flow", scale = 28316846592e-12, default = "m3/s", }, ["cumi/a"] = { name1 = "cubic mile per annum", name2 = "cubic miles per annum", symbol = "cu&nbsp;mi/a", utype = "flow", scale = 132.08171170940057, default = "km3/a", link = "Cubic foot per second", }, ["cuyd/h"] = { name1 = "cubic yard per hour", name2 = "cubic yards per hour", symbol = "cuyd/h", utype = "flow", scale = 0.00021237634944000001, default = "m3/h", link = "Cubic foot per second", }, ["cuyd/s"] = { name1 = "cubic yard per second", name2 = "cubic yards per second", symbol = "cu&nbsp;yd/s", utype = "flow", scale = 0.76455485798400002, default = "m3/s", }, ["Goilbbl/a"] = { name1 = "billion barrels per year", name2 = "billion barrels per year", symbol = "Gbbl/a", utype = "flow", scale = 5.0380033629933836, default = "v * 1.58987294928 < 10 ! e6 ! e9 ! m3/a", link = "Barrel per day", }, ["impgal/h"] = { name1 = "imperial gallon per hour", name2 = "imperial gallons per hour", symbol = "imp&nbsp;gal/h", utype = "flow", scale = 1.2628027777777779e-6, default = "m3/h", link = "Gallon", }, ["impgal/min"] = { name1 = "imperial gallon per minute", name2 = "imperial gallons per minute", symbol = "imp gal/min", utype = "flow", scale = 7.5768166666666671e-5, default = "m3/s", link = "Gallon", }, ["impgal/s"] = { name1 = "imperial gallon per second", name2 = "imperial gallons per second", symbol = "impgal/s", utype = "flow", scale = 0.00454609, default = "m3/s", link = "Imperial gallons per second", }, ["km3/a"] = { name1 = "cubic kilometre per annum", name1_us = "cubic kilometer per annum", name2 = "cubic kilometres per annum", name2_us = "cubic kilometers per annum", symbol = "km<sup>3</sup>/a", utype = "flow", scale = 31.68808781402895, default = "cumi/a", link = "Cubic metre per second", }, ["km3/d"] = { name1 = "cubic kilometre per day", name1_us = "cubic kilometer per day", name2 = "cubic kilometres per day", name2_us = "cubic kilometers per day", symbol = "km<sup>3</sup>/d", utype = "flow", scale = 11574.074074074075, default = "cuft/d", link = "Cubic metre per second", }, ["koilbbl/a"] = { name1 = "thousand barrels per year", name2 = "thousand barrels per year", symbol = "kbbl/a", utype = "flow", scale = 5.0380033629933841e-6, default = "v * 1.58987294928 < 10 ! ! e3 ! m3/a", link = "Barrel per day", }, ["koilbbl/d"] = { name1 = "thousand barrels per day", name2 = "thousand barrels per day", symbol = "kbbl/d", utype = "flow", scale = 0.0018401307283333335, default = "v * 1.58987294928 < 10 ! ! e3 ! m3/d", link = "Barrel per day", }, ["L/h"] = { name1 = "litre per hour", name1_us = "liter per hour", name2 = "litres per hour", name2_us = "liters per hour", symbol = "L/h", utype = "flow", scale = 2.7777777777777776e-7, default = "impgal/h USgal/h", link = "Cubic metre per second", }, ["L/min"] = { name1 = "litre per minute", name1_us = "liter per minute", name2 = "litres per minute", name2_us = "liters per minute", symbol = "L/min", utype = "flow", scale = 1.6666666666666667e-5, default = "impgal/min USgal/min", link = "Cubic metre per second", }, ["L/s"] = { name1 = "litre per second", name1_us = "liter per second", name2 = "litres per second", name2_us = "liters per second", symbol = "L/s", utype = "flow", scale = 0.001, default = "cuft/s", link = "Cubic metre per second", }, ["m3/a"] = { name1 = "cubic metre per annum", name1_us = "cubic meter per annum", name2 = "cubic metres per annum", name2_us = "cubic meters per annum", symbol = "m<sup>3</sup>/a", utype = "flow", scale = 3.1688087814028947e-8, default = "cuft/a", link = "Cubic metre per second", }, ["m3/d"] = { name1 = "cubic metre per day", name1_us = "cubic meter per day", name2 = "cubic metres per day", name2_us = "cubic meters per day", symbol = "m<sup>3</sup>/d", utype = "flow", scale = 1.1574074074074073e-5, default = "cuft/d", link = "Cubic metre per second", }, ["m3/h"] = { name1 = "cubic metre per hour", name1_us = "cubic meter per hour", name2 = "cubic metres per hour", name2_us = "cubic meters per hour", symbol = "m<sup>3</sup>/h", utype = "flow", scale = 0.00027777777777777778, default = "cuft/h", link = "Cubic metre per second", }, ["m3/min"] = { name1 = "cubic metre per minute", name1_us = "cubic meter per minute", name2 = "cubic metres per minute", name2_us = "cubic meters per minute", symbol = "m<sup>3</sup>/min", utype = "flow", scale = 0.016666666666666666, default = "cuft/min", link = "Cubic metre per second", }, ["m3/s"] = { name1 = "cubic metre per second", name1_us = "cubic meter per second", name2 = "cubic metres per second", name2_us = "cubic meters per second", symbol = "m<sup>3</sup>/s", utype = "flow", scale = 1, default = "cuft/s", }, ["Moilbbl/a"] = { name1 = "million barrels per year", name2 = "million barrels per year", symbol = "Mbbl/a", utype = "flow", scale = 0.0050380033629933837, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3/a", link = "Barrel per day", }, ["Moilbbl/d"] = { name1 = "million barrels per day", name2 = "million barrels per day", symbol = "Mbbl/d", utype = "flow", scale = 1.8401307283333335, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3/d", link = "Barrel per day", }, ["oilbbl/a"] = { name1 = "barrel per year", name2 = "barrels per year", symbol = "bbl/a", utype = "flow", scale = 5.0380033629933841e-9, default = "m3/a", link = "Barrel per day", }, ["oilbbl/d"] = { name1 = "barrel per day", name2 = "barrels per day", symbol = "bbl/d", utype = "flow", scale = 1.8401307283333336e-6, default = "m3/d", }, ["Toilbbl/a"] = { name1 = "trillion barrels per year", name2 = "trillion barrels per year", symbol = "Tbbl/a", utype = "flow", scale = 5038.0033629933832, default = "v * 1.58987294928 < 10 ! e9 ! e12 ! m3/a", link = "Barrel per day", }, ["U.S.gal/d"] = { name1 = "U.S. gallon per day", name2 = "U.S. gallons per day", symbol = "U.S.&nbsp;gal/d", utype = "flow", scale = 4.3812636388888893e-8, default = "m3/s", customary= 1, }, ["U.S.gal/h"] = { name1 = "gallon per hour", name2 = "gallons per hour", symbol = "gal/h", utype = "flow", scale = 1.0515032733333334e-6, default = "m3/h", link = "Gallon", customary= 2, }, ["U.S.gal/min"] = { name1 = "U.S. gallon per minute", name2 = "U.S. gallons per minute", symbol = "U.S.&nbsp;gal/min", utype = "flow", scale = 6.3090196400000003e-5, default = "m3/s", link = "Gallon", }, ["USgal/a"] = { name1 = "US gallon per year", name2 = "US gallons per year", symbol = "US&nbsp;gal/a", utype = "flow", scale = 1.1995246102365199e-10, default = "m3/s", }, ["USgal/d"] = { name1 = "US gallon per day", name2 = "US gallons per day", symbol = "US&nbsp;gal/d", utype = "flow", scale = 4.3812636388888893e-8, default = "m3/s", }, ["USgal/h"] = { name1 = "gallon per hour", name2 = "gallons per hour", symbol = "gal/h", utype = "flow", scale = 1.0515032733333334e-6, default = "m3/h", link = "Gallon", customary= 1, }, ["USgal/min"] = { name1 = "US gallon per minute", name2 = "US gallons per minute", symbol = "US&nbsp;gal/min", utype = "flow", scale = 6.3090196400000003e-5, default = "m3/s", link = "Gallon", }, ["USgal/s"] = { name1 = "US gallon per second", name1_us = "U.S. gallon per second", name2 = "US gallons per second", name2_us = "U.S. gallons per second", symbol = "USgal/s", utype = "flow", scale = 0.003785411784, default = "m3/s", link = "US gallons per second", }, ["ft3/a"] = { target = "cuft/a", }, ["ft3/d"] = { target = "cuft/d", }, ["ft3/h"] = { target = "cuft/h", }, ["ft3/s"] = { target = "cuft/s", }, ["Gcuft/a"] = { target = "e9cuft/a", }, ["Gcuft/d"] = { target = "e9cuft/d", }, ["kcuft/a"] = { target = "e3cuft/a", }, ["kcuft/d"] = { target = "e3cuft/d", }, ["kcuft/s"] = { target = "e3cuft/s", }, ["Mcuft/a"] = { target = "e6cuft/a", }, ["Mcuft/d"] = { target = "e6cuft/d", }, ["Mcuft/s"] = { target = "e6cuft/s", }, ["m³/s"] = { target = "m3/s", }, ["Tcuft/a"] = { target = "e12cuft/a", }, ["Tcuft/d"] = { target = "e12cuft/d", }, ["u.s.gal/min"] = { target = "U.S.gal/min", }, ["usgal/min"] = { target = "USgal/min", }, ["-LTf"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "LTf", utype = "force", scale = 9964.01641818352, default = "kN", }, ["-STf"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "STf", utype = "force", scale = 8896.443230521, default = "kN", }, ["dyn"] = { name1 = "dyne", symbol = "dyn", utype = "force", scale = 0.00001, default = "gr-f", }, ["g-f"] = { name1 = "gram-force", name2 = "grams-force", symbol = "g<sub>f</sub>", utype = "force", scale = 0.00980665, default = "mN oz-f", link = "Kilogram-force", }, ["gf"] = { name1 = "gram-force", name2 = "grams-force", symbol = "gf", utype = "force", scale = 0.00980665, default = "mN ozf", link = "Kilogram-force", }, ["gr-f"] = { name1 = "grain-force", name2 = "grains-force", symbol = "gr<sub>f</sub>", utype = "force", scale = 0.0006354602307515, default = "μN", link = "Pound (force)", }, ["grf"] = { name1 = "grain-force", name2 = "grains-force", symbol = "grf", utype = "force", scale = 0.0006354602307515, default = "μN", link = "Pound (force)", }, ["kdyn"] = { name1 = "kilodyne", symbol = "kdyn", utype = "force", scale = 0.01, default = "oz-f", link = "Dyne", }, ["kg-f"] = { name1 = "kilogram-force", name2 = "kilograms-force", symbol = "kg<sub>f</sub>", utype = "force", scale = 9.80665, default = "N lb-f", }, ["kgf"] = { name1 = "kilogram-force", name2 = "kilograms-force", symbol = "kgf", utype = "force", scale = 9.80665, default = "N lbf", }, ["kp"] = { name1 = "kilopond", symbol = "kp", utype = "force", scale = 9.80665, default = "N lb-f", link = "Kilogram-force", }, ["L/T-f"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "L/T<sub>f</sub>", utype = "force", scale = 9964.01641818352, default = "kN", }, ["L/Tf"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "L/Tf", utype = "force", scale = 9964.01641818352, default = "kN", }, ["lb-f"] = { name1 = "pound-force", name2 = "pounds-force", symbol = "lb<sub>f</sub>", utype = "force", scale = 4.4482216152605, default = "N", link = "Pound (force)", }, ["lbf"] = { name1 = "pound-force", name2 = "pounds-force", symbol = "lbf", utype = "force", scale = 4.4482216152605, default = "N", link = "Pound (force)", }, ["lb(f)"] = { name1 = "pound", symbol = "lb", utype = "force", scale = 4.4482216152605, default = "N", link = "Pound (force)", }, ["LT-f"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "LT<sub>f</sub>", utype = "force", scale = 9964.01641818352, default = "kN", }, ["LTf"] = { name1 = "long ton-force", name2 = "long tons-force", symbol = "LTf", usename = 1, utype = "force", scale = 9964.01641818352, default = "kN", }, ["Mdyn"] = { name1 = "megadyne", symbol = "Mdyn", utype = "force", scale = 10, default = "lb-f", link = "Dyne", }, ["mdyn"] = { name1 = "millidyne", symbol = "mdyn", utype = "force", scale = 0.00000001, default = "gr-f", link = "Dyne", }, ["mg-f"] = { name1 = "milligram-force", name2 = "milligrams-force", symbol = "mg<sub>f</sub>", utype = "force", scale = 0.00000980665, default = "μN gr-f", link = "Kilogram-force", }, ["mgf"] = { name1 = "milligram-force", name2 = "milligrams-force", symbol = "mgf", utype = "force", scale = 0.00000980665, default = "μN grf", link = "Kilogram-force", }, ["Mp"] = { name1 = "megapond", symbol = "Mp", utype = "force", scale = 9806.65, default = "kN LT-f ST-f", link = "Kilogram-force", }, ["mp"] = { name1 = "millipond", symbol = "mp", utype = "force", scale = 0.00000980665, default = "μN gr-f", link = "Kilogram-force", }, ["N"] = { _name1 = "newton", _symbol = "N", utype = "force", scale = 1, prefixes = 1, default = "lb-f", link = "Newton (unit)", }, ["oz-f"] = { name1 = "ounce-force", name2 = "ounces-force", symbol = "oz<sub>f</sub>", utype = "force", scale = 0.2780138203095378125, default = "mN", link = "Pound (force)", }, ["ozf"] = { name1 = "ounce-force", name2 = "ounces-force", symbol = "ozf", utype = "force", scale = 0.2780138203095378125, default = "mN", link = "Pound (force)", }, ["p"] = { name1 = "pond", symbol = "p", utype = "force", scale = 0.00980665, default = "mN oz-f", link = "Kilogram-force", }, ["pdl"] = { name1 = "poundal", symbol = "pdl", utype = "force", scale = 0.138254954376, default = "N", }, ["S/T-f"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "S/T<sub>f</sub>", utype = "force", scale = 8896.443230521, default = "kN", }, ["S/Tf"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "S/Tf", utype = "force", scale = 8896.443230521, default = "kN", }, ["ST-f"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "ST<sub>f</sub>", utype = "force", scale = 8896.443230521, default = "kN", }, ["STf"] = { name1 = "short ton-force", name2 = "short tons-force", symbol = "STf", usename = 1, utype = "force", scale = 8896.443230521, default = "kN", }, ["t-f"] = { name1 = "tonne-force", name2 = "tonnes-force", symbol = "t<sub>f</sub>", utype = "force", scale = 9806.65, default = "kN LT-f ST-f", link = "Ton-force#Tonne-force", }, ["tf"] = { name1 = "tonne-force", name2 = "tonnes-force", symbol = "tf", utype = "force", scale = 9806.65, default = "kN LTf STf", link = "Ton-force#Tonne-force", }, ["dyne"] = { target = "dyn", }, ["newtons"] = { target = "N", }, ["poundal"] = { target = "pdl", }, ["tonne-force"] = { target = "tf", }, ["impgal/mi"] = { per = { "@impgal", "mi" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "L/km USgal/mi", }, ["km/L"] = { per = { "km", "L" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "mpgimp mpgus", }, ["km/l"] = { per = { "km", "ll" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "mpgimp mpgus", }, ["L/100 km"] = { per = { "L", "100km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", symlink = "[[Fuel economy in automobiles#Units of measure|L/100&nbsp;km]]", }, ["l/100 km"] = { per = { "ll", "100km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", symlink = "[[Fuel economy in automobiles#Units of measure|l/100&nbsp;km]]", }, ["L/km"] = { per = { "L", "km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", }, ["l/km"] = { per = { "ll", "km" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "mpgimp mpgus", }, ["mi/impqt"] = { per = { "mi", "impqt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mi/U.S.qt"] = { per = { "mi", "U.S.qt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mi/USqt"] = { per = { "mi", "USqt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mi/usqt"] = { per = { "mi", "usqt" }, utype = "fuel efficiency", invert = -1, iscomplex= true, default = "km/L", }, ["mpgimp"] = { per = { "mi", "@impgal" }, symbol = "mpg<sub>&#8209;imp</sub>", utype = "fuel efficiency", invert = -1, iscomplex= true, default = "L/100 km+mpgus", symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[Imperial units|imp]]</sub>", }, ["mpgus"] = { per = { "mi", "+USgal" }, symbol = "mpg<sub>&#8209;US</sub>", utype = "fuel efficiency", invert = -1, iscomplex= true, default = "L/100 km+mpgimp", symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[United States customary units|US]]</sub>", }, ["U.S.gal/mi"] = { per = { "*U.S.gal", "mi" }, sp_us = true, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "L/km impgal/mi", }, ["usgal/mi"] = { per = { "+USgal", "mi" }, utype = "fuel efficiency", invert = 1, iscomplex= true, default = "L/km impgal/mi", }, ["L/100km"] = { target = "L/100 km", }, ["l/100km"] = { target = "l/100 km", }, ["mpg"] = { shouldbe = "Use %{mpgus%} for miles per US gallon or %{mpgimp%} for miles per imperial gallon (not %{mpg%})", }, ["mpgU.S."] = { target = "mpgus", symbol = "mpg<sub>&#8209;U.S.</sub>", sp_us = true, symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[United States customary units|U.S.]]</sub>", }, ["mpgu.s."] = { target = "mpgus", symbol = "mpg<sub>&#8209;U.S.</sub>", sp_us = true, symlink = "[[Fuel economy in automobiles#Units of measure|mpg]]<sub>&#8209;[[United States customary units|U.S.]]</sub>", }, ["mpgUS"] = { target = "mpgus", }, ["USgal/mi"] = { target = "usgal/mi", }, ["kPa/m"] = { per = { "kPa", "-m-frac" }, utype = "fracture gradient", default = "psi/ft", }, ["psi/ft"] = { per = { "psi", "-ft-frac" }, utype = "fracture gradient", default = "kPa/m", }, ["cm/km"] = { name1 = "centimetre per kilometre", name1_us = "centimeter per kilometer", name2 = "centimetres per kilometre", name2_us = "centimeters per kilometer", symbol = "cm/km", utype = "gradient", scale = 0.00001, default = "ft/mi", link = "Grade (slope)", }, ["ft/mi"] = { name1 = "foot per mile", name2 = "feet per mile", symbol = "ft/mi", utype = "gradient", scale = 0.00018939393939393939, default = "v < 5.28 ! c ! ! m/km", link = "Grade (slope)", }, ["ft/nmi"] = { name1 = "foot per nautical mile", name2 = "feet per nautical mile", symbol = "ft/nmi", utype = "gradient", scale = 0.00016457883369330455, default = "v < 6.076 ! c ! ! m/km", link = "Grade (slope)", }, ["in/ft"] = { name1 = "inch per foot", name2 = "inches per foot", symbol = "in/ft", utype = "gradient", scale = 0.083333333333333329, default = "mm/m", link = "Grade (slope)", }, ["in/mi"] = { name1 = "inch per mile", name2 = "inches per mile", symbol = "in/mi", utype = "gradient", scale = 1.5782828282828283e-5, default = "v < 0.6336 ! m ! c ! m/km", link = "Grade (slope)", }, ["m/km"] = { name1 = "metre per kilometre", name1_us = "meter per kilometer", name2 = "metres per kilometre", name2_us = "meters per kilometer", symbol = "m/km", utype = "gradient", scale = 0.001, default = "ft/mi", link = "Grade (slope)", }, ["mm/km"] = { name1 = "millimetre per kilometre", name1_us = "millimeter per kilometer", name2 = "millimetres per kilometre", name2_us = "millimeters per kilometer", symbol = "mm/km", utype = "gradient", scale = 0.000001, default = "in/mi", link = "Grade (slope)", }, ["mm/m"] = { name1 = "millimetre per metre", name1_us = "millimeter per meter", name2 = "millimetres per metre", name2_us = "millimeters per meter", symbol = "mm/m", utype = "gradient", scale = 0.001, default = "in/ft", link = "Grade (slope)", }, ["admi"] = { name1 = "admiralty mile", symbol = "nmi&nbsp;(admiralty)", utype = "length", scale = 1853.184, default = "km mi", link = "Nautical mile", }, ["AU"] = { name1 = "astronomical unit", symbol = "AU", utype = "length", scale = 149597870700, default = "km mi", }, ["Brnmi"] = { name1 = "British nautical mile", symbol = "(Brit)&nbsp;nmi", utype = "length", scale = 1853.184, default = "km mi", link = "Nautical mile", }, ["bu"] = { name2 = "bu", symbol = "bu", usename = 1, utype = "length", scale = 0.0030303030303030303, default = "mm", link = "Japanese units of measurement#Length", }, ["ch"] = { name1 = "chain", symbol = "ch", utype = "length", scale = 20.1168, default = "ft m", subdivs = { ["ft"] = { 66, default = "m" }, ["yd"] = { 22, default = "m" } }, link = "Chain (unit)", }, ["chlk"] = { name1 = "[[Chain (unit)|chain]]", symbol = "[[Chain (unit)|ch]]", utype = "length", scale = 20.1168, default = "ft m", link = "", }, ["chain"] = { symbol = "chain", usename = 1, utype = "length", scale = 20.1168, default = "ft m", subdivs = { ["ft"] = { 66, default = "m" }, ["yd"] = { 22, default = "m" } }, link = "Chain (unit)", }, ["chainlk"] = { symbol = "[[Chain (unit)|chain]]", usename = 1, utype = "length", scale = 20.1168, default = "ft m", link = "", }, ["dpcm"] = { name2 = "dot/cm", symbol = "dot/cm", utype = "length", scale = 100, invert = -1, iscomplex= true, default = "dpi", link = "Dots per inch", }, ["dpi"] = { name2 = "DPI", symbol = "DPI", utype = "length", scale = 39.370078740157481, invert = -1, iscomplex= true, default = "pitch", link = "Dots per inch", }, ["fathom"] = { symbol = "fathom", usename = 1, utype = "length", scale = 1.8288, default = "ft m", }, ["foot"] = { name1 = "foot", name2 = "foot", symbol = "ft", utype = "length", scale = 0.3048, default = "m", subdivs = { ["in"] = { 12, default = "m" } }, link = "Foot (unit)", }, ["ft"] = { name1 = "foot", name2 = "feet", symbol = "ft", utype = "length", scale = 0.3048, exception= "integer_more_precision", default = "m", subdivs = { ["in"] = { 12, default = "m" } }, link = "Foot (unit)", }, ["furlong"] = { symbol = "furlong", usename = 1, utype = "length", scale = 201.168, default = "ft m", }, ["Gly"] = { name1 = "gigalight-year", symbol = "Gly", utype = "length", scale = 9.4607304725808e24, default = "Mpc", link = "Light-year#Definitions", }, ["Gpc"] = { name1 = "gigaparsec", symbol = "Gpc", utype = "length", scale = 3.0856775814671916e25, default = "Gly", link = "Parsec#Megaparsecs and gigaparsecs", }, ["hand"] = { name1 = "hand", symbol = "h", utype = "length", builtin = "hand", scale = 0.1016, iscomplex= true, default = "in cm", link = "Hand (unit)", }, ["in"] = { name1 = "inch", name2 = "inches", symbol = "in", utype = "length", scale = 0.0254, exception= "subunit_more_precision", default = "mm", }, ["inabbreviated"] = { name2 = "in", symbol = "in", utype = "length", scale = 0.0254, default = "mm", link = "Inch", }, ["kly"] = { name1 = "kilolight-year", symbol = "kly", utype = "length", scale = 9.4607304725808e18, default = "pc", link = "Light-year#Definitions", }, ["kpc"] = { name1 = "kiloparsec", symbol = "kpc", utype = "length", scale = 3.0856775814671916e19, default = "kly", link = "Parsec#Parsecs and kiloparsecs", }, ["LD"] = { name1 = "lunar distance", symbol = "LD", utype = "length", scale = 384403000, default = "km mi", link = "Lunar distance (astronomy)", }, ["league"] = { symbol = "league", usename = 1, utype = "length", scale = 4828.032, default = "km", link = "League (unit)", }, ["ly"] = { name1 = "light-year", symbol = "ly", utype = "length", scale = 9.4607304725808e15, default = "AU", }, ["m"] = { _name1 = "metre", _name1_us= "meter", _symbol = "m", utype = "length", scale = 1, prefixes = 1, default = "v > 0 and v < 3 ! ftin ! ft", link = "Metre", }, ["mi"] = { name1 = "mile", symbol = "mi", utype = "length", scale = 1609.344, default = "km", subdivs = { ["ch"] = { 80, default = "km" }, ["chlk"] = { 80, default = "km" }, ["chain"] = { 80, default = "km" }, ["chainlk"] = { 80, default = "km" }, ["ft"] = { 5280, default = "km" }, ["furlong"] = { 8, default = "km" }, ["yd"] = { 1760, default = "km" } }, }, ["mil"] = { symbol = "mil", usename = 1, utype = "length", scale = 0.0000254, default = "mm", link = "Thousandth of an inch", }, ["Mly"] = { name1 = "megalight-year", symbol = "Mly", utype = "length", scale = 9.4607304725808e21, default = "kpc", link = "Light-year#Definitions", }, ["Mpc"] = { name1 = "megaparsec", symbol = "Mpc", utype = "length", scale = 3.0856775814671916e22, default = "Mly", link = "Parsec#Megaparsecs and gigaparsecs", }, ["NM"] = { name1 = "nautical mile", symbol = "NM", utype = "length", scale = 1852, default = "km mi", }, ["nmi"] = { name1 = "nautical mile", symbol = "nmi", utype = "length", scale = 1852, default = "km mi", }, ["oldUKnmi"] = { name1 = "nautical mile", symbol = "nmi", utype = "length", scale = 1853.184, default = "km mi", }, ["oldUSnmi"] = { name1 = "nautical mile", symbol = "nmi", utype = "length", scale = 1853.24496, default = "km mi", }, ["pc"] = { name1 = "parsec", symbol = "pc", utype = "length", scale = 3.0856775814671916e16, default = "ly", }, ["perch"] = { name2 = "perches", symbol = "perch", usename = 1, utype = "length", scale = 5.0292, default = "ft m", link = "Rod (unit)", }, ["pitch"] = { name2 = "μm", symbol = "μm", utype = "length", scale = 1e-6, default = "dpi", defkey = "pitch", linkey = "pitch", link = "Dots per inch", }, ["pole"] = { symbol = "pole", usename = 1, utype = "length", scale = 5.0292, default = "ft m", link = "Rod (unit)", }, ["pre1954U.S.nmi"] = { name1 = "(pre-1954&nbsp;U.S.) nautical mile", symbol = "(pre&#8209;1954&nbsp;U.S.) nmi", utype = "length", scale = 1853.24496, default = "km mi", link = "Nautical mile", }, ["pre1954USnmi"] = { name1 = "(pre-1954&nbsp;US) nautical mile", name1_us = "(pre-1954&nbsp;U.S.) nautical mile", symbol = "(pre&#8209;1954&nbsp;US) nmi", sym_us = "(pre&#8209;1954&nbsp;U.S.) nmi", utype = "length", scale = 1853.24496, default = "km mi", link = "Nautical mile", }, ["rd"] = { name1 = "rod", symbol = "rd", utype = "length", scale = 5.0292, default = "ft m", link = "Rod (unit)", }, ["royal cubit"] = { name1 = "royal cubit", symbol = "cu", utype = "length", scale = 0.524, default = "mm", }, ["rtkm"] = { name1 = "route kilometre", name1_us = "route kilometer", symbol = "km", utype = "length", scale = 1000, default = "mi", link = "Kilometre", }, ["rtmi"] = { name1 = "route mile", symbol = "mi", utype = "length", scale = 1609.344, default = "km", link = "Mile", }, ["shaku"] = { name2 = "shaku", symbol = "shaku", usename = 1, utype = "length", scale = 0.30303030303030304, default = "m", link = "Shaku (unit)", }, ["sm"] = { name1 = "smoot", symbol = "sm", utype = "length", scale = 1.70180, default = "m", link = "Smoot (unit)", }, ["smi"] = { name1 = "statute mile", symbol = "mi", utype = "length", scale = 1609.344, default = "km", subdivs = { ["chain"] = { 80, default = "km" } }, }, ["solar radius"] = { name1 = "solar radius", name2 = "solar radii", symbol = "''R''<sub>☉</sub>", utype = "length", scale = 695700e3, default = "km", }, ["sun"] = { name2 = "sun", symbol = "sun", usename = 1, utype = "length", scale = 0.030303030303030304, default = "mm", link = "Japanese units of measurement#Length", }, ["thou"] = { name2 = "thou", symbol = "thou", usename = 1, utype = "length", scale = 0.0000254, default = "mm", link = "Thousandth of an inch", }, ["verst"] = { symbol = "verst", usename = 1, utype = "length", scale = 1066.8, default = "km mi", }, ["yd"] = { name1 = "yard", symbol = "yd", utype = "length", scale = 0.9144, default = "m", subdivs = { ["ft"] = { 3, default = "m" } }, }, ["μin"] = { name1 = "microinch", name2 = "microinches", symbol = "μin", utype = "length", scale = 0.0000000254, default = "nm", link = "SI prefix#Non-metric units", }, ["Å"] = { name1 = "ångström", symbol = "Å", utype = "length", scale = 0.0000000001, default = "in", }, ["Hz"] = { _name1 = "hertz", _name2 = "hertz", _symbol = "Hz", utype = "length", scale = 3.3356409519815204e-9, invert = -1, iscomplex= true, prefixes = 1, default = "m", link = "Hertz", }, ["rpm"] = { name1 = "revolution per minute", name2 = "revolutions per minute", symbol = "rpm", utype = "length", scale = 5.5594015866358675e-11, invert = -1, iscomplex= true, default = "Hz", link = "Revolutions per minute", }, ["-ft-frac"] = { target = "ft", link = "Fracture gradient", }, ["-in-stiff"] = { target = "in", link = "Stiffness", }, ["-m-frac"] = { target = "m", link = "Fracture gradient", }, ["-m-stiff"] = { target = "m", link = "Stiffness", }, ["100km"] = { target = "km", multiplier= 100, }, ["100mi"] = { target = "mi", multiplier= 100, }, ["100miles"] = { target = "mi", symbol = "miles", multiplier= 100, }, ["admiralty nmi"] = { target = "oldUKnmi", }, ["angstrom"] = { target = "Å", }, ["au"] = { target = "AU", symbol = "au", }, ["feet"] = { target = "ft", }, ["hands"] = { target = "hand", }, ["inch"] = { target = "in", }, ["inches"] = { target = "in", }, ["light-year"] = { target = "ly", }, ["meter"] = { target = "m", sp_us = true, }, ["meters"] = { target = "m", sp_us = true, }, ["metre"] = { target = "m", }, ["metres"] = { target = "m", }, ["micrometre"] = { target = "μm", }, ["micron"] = { target = "μm", default = "μin", }, ["mile"] = { target = "mi", }, ["miles"] = { target = "mi", }, ["parsec"] = { target = "pc", }, ["rod"] = { target = "rd", }, ["smoot"] = { target = "sm", }, ["uin"] = { target = "μin", }, ["yard"] = { target = "yd", }, ["yards"] = { target = "yd", }, ["yds"] = { target = "yd", }, ["dtex"] = { name1 = "decitex", name2 = "decitex", symbol = "dtex", utype = "linear density", scale = 1e-7, default = "lb/yd", link = "Units of textile measurement#Units", }, ["kg/cm"] = { name1 = "kilogram per centimetre", name1_us = "kilogram per centimeter", name2 = "kilograms per centimetre", name2_us = "kilograms per centimeter", symbol = "kg/cm", utype = "linear density", scale = 100, default = "lb/yd", link = "Linear density", }, ["kg/m"] = { name1 = "kilogram per metre", name1_us = "kilogram per meter", name2 = "kilograms per metre", name2_us = "kilograms per meter", symbol = "kg/m", utype = "linear density", scale = 1, default = "lb/yd", link = "Linear density", }, ["lb/ft"] = { name1 = "pound per foot", name2 = "pounds per foot", symbol = "lb/ft", utype = "linear density", scale = 1.4881639435695539, default = "kg/m", link = "Linear density", }, ["lb/yd"] = { name1 = "pound per yard", name2 = "pounds per yard", symbol = "lb/yd", utype = "linear density", scale = 0.49605464785651798, default = "kg/m", link = "Linear density", }, ["G"] = { _name1 = "gauss", _name2 = "gauss", _symbol = "G", utype = "magnetic field strength", scale = 0.0001, prefixes = 1, default = "T", link = "Gauss (unit)", }, ["T"] = { _name1 = "tesla", _symbol = "T", utype = "magnetic field strength", scale = 1, prefixes = 1, default = "G", link = "Tesla (unit)", }, ["A/m"] = { name1 = "ampere per metre", name1_us = "ampere per meter", name2 = "amperes per metre", name2_us = "amperes per meter", symbol = "A/m", utype = "magnetizing field", scale = 1, default = "Oe", }, ["kA/m"] = { name1 = "kiloampere per metre", name1_us = "kiloampere per meter", name2 = "kiloamperes per metre", name2_us = "kiloamperes per meter", symbol = "kA/m", utype = "magnetizing field", scale = 1000, default = "kOe", link = "Ampere per metre", }, ["MA/m"] = { name1 = "megaampere per metre", name1_us = "megaampere per meter", name2 = "megaamperes per metre", name2_us = "megaamperes per meter", symbol = "MA/m", utype = "magnetizing field", scale = 1e6, default = "kOe", link = "Ampere per metre", }, ["Oe"] = { _name1 = "oersted", _symbol = "Oe", utype = "magnetizing field", scale = 79.5774715, prefixes = 1, default = "kA/m", link = "Oersted", }, ["-Lcwt"] = { name1 = "hundredweight", name2 = "hundredweight", symbol = "cwt", utype = "mass", scale = 50.80234544, default = "lb", }, ["-Scwt"] = { name1 = "hundredweight", name2 = "hundredweight", symbol = "cwt", utype = "mass", scale = 45.359237, default = "lb", }, ["-ST"] = { name1 = "short ton", symbol = "ST", utype = "mass", scale = 907.18474, default = "t", }, ["carat"] = { symbol = "carat", usename = 1, utype = "mass", scale = 0.0002, default = "g", link = "Carat (mass)", }, ["drachm"] = { name1_us = "dram", symbol = "drachm", usename = 1, utype = "mass", scale = 0.001771845195, default = "g", link = "Dram (unit)", }, ["dram"] = { target = "drachm", }, ["dwt"] = { name1 = "pennyweight", symbol = "dwt", utype = "mass", scale = 0.00155517384, default = "oz g", }, ["DWton"] = { symbol = "deadweight ton", usename = 1, utype = "mass", scale = 1016.0469088, default = "DWtonne", link = "Deadweight tonnage", }, ["DWtonne"] = { name1_us = "deadweight metric ton", symbol = "deadweight tonne", sym_us = "~deadweight metric ton", usename = 1, utype = "mass", scale = 1000, default = "DWton", link = "Deadweight tonnage", }, ["g"] = { _name1 = "gram", _symbol = "g", utype = "mass", scale = 0.001, prefixes = 1, default = "oz", link = "Gram", }, ["gr"] = { name1 = "grain", symbol = "gr", utype = "mass", scale = 0.00006479891, default = "g", link = "Grain (unit)", }, ["Gt"] = { name1 = "gigatonne", symbol = "Gt", utype = "mass", scale = 1000000000000, default = "LT ST", link = "Tonne", }, ["impgalh2o"] = { name1 = "imperial gallon of water", name2 = "imperial gallons of water", symbol = "imp&nbsp;gal H<sub>2</sub>O", utype = "mass", scale = 4.5359236999999499, default = "lb kg", link = "Imperial gallon", }, ["kt"] = { name1 = "kilotonne", symbol = "kt", utype = "mass", scale = 1000000, default = "LT ST", link = "Tonne", }, ["lb"] = { name1 = "pound", symbol = "lb", utype = "mass", scale = 0.45359237, exception= "integer_more_precision", default = "kg", subdivs = { ["oz"] = { 16, default = "kg" } }, link = "Pound (mass)", }, ["Lcwt"] = { name1 = "long hundredweight", name2 = "long hundredweight", symbol = "Lcwt", usename = 1, utype = "mass", scale = 50.80234544, default = "lb", subdivs = { ["qtr"] = { 4, default = "kg" }, ["st"] = { 8, default = "kg" } }, link = "Hundredweight", }, ["long cwt"] = { name1 = "long hundredweight", name2 = "long hundredweight", symbol = "long&nbsp;cwt", utype = "mass", scale = 50.80234544, default = "lb kg", subdivs = { ["qtr"] = { 4, default = "kg" } }, link = "Hundredweight", }, ["long qtr"] = { name1 = "long quarter", symbol = "long&nbsp;qtr", utype = "mass", scale = 12.70058636, default = "lb kg", }, ["LT"] = { symbol = "long ton", usename = 1, utype = "mass", scale = 1016.0469088, default = "t", subdivs = { ["Lcwt"] = { 20, default = "t", unit = "-Lcwt" } }, }, ["lt"] = { name1 = "long ton", symbol = "LT", utype = "mass", scale = 1016.0469088, default = "t", subdivs = { ["Lcwt"] = { 20, default = "t", unit = "-Lcwt" } }, }, ["metric ton"] = { symbol = "metric ton", usename = 1, utype = "mass", scale = 1000, default = "long ton", link = "Tonne", }, ["MT"] = { name1 = "metric ton", symbol = "t", utype = "mass", scale = 1000, default = "LT ST", link = "Tonne", }, ["Mt"] = { name1 = "megatonne", symbol = "Mt", utype = "mass", scale = 1000000000, default = "LT ST", link = "Tonne", }, ["oz"] = { name1 = "ounce", symbol = "oz", utype = "mass", scale = 0.028349523125, default = "g", }, ["ozt"] = { name1 = "troy ounce", symbol = "ozt", utype = "mass", scale = 0.0311034768, default = "oz g", }, ["pdr"] = { name1 = "pounder", symbol = "pdr", utype = "mass", scale = 0.45359237, default = "kg", link = "Pound (mass)", }, ["qtr"] = { name1 = "quarter", symbol = "qtr", utype = "mass", scale = 12.70058636, default = "lb kg", subdivs = { ["lb"] = { 28, default = "kg" } }, link = "Long quarter", }, ["Scwt"] = { name1 = "short hundredweight", name2 = "short hundredweight", symbol = "Scwt", usename = 1, utype = "mass", scale = 45.359237, default = "lb", link = "Hundredweight", }, ["short cwt"] = { name1 = "short hundredweight", name2 = "short hundredweight", symbol = "short&nbsp;cwt", utype = "mass", scale = 45.359237, default = "lb kg", link = "Hundredweight", }, ["short qtr"] = { name1 = "short quarter", symbol = "short&nbsp;qtr", utype = "mass", scale = 11.33980925, default = "lb kg", }, ["ST"] = { symbol = "short ton", usename = 1, utype = "mass", scale = 907.18474, default = "t", subdivs = { ["Scwt"] = { 20, default = "t", unit = "-Scwt" } }, }, ["shtn"] = { name1 = "short ton", symbol = "sh&nbsp;tn", utype = "mass", scale = 907.18474, default = "t", }, ["shton"] = { symbol = "ton", usename = 1, utype = "mass", scale = 907.18474, default = "t", }, ["solar mass"] = { name1 = "solar mass", name2 = "solar masses", symbol = "''M''<sub>☉</sub>", utype = "mass", scale = 1.98855e30, default = "kg", }, ["st"] = { name1 = "stone", name2 = "stone", symbol = "st", utype = "mass", scale = 6.35029318, default = "lb kg", subdivs = { ["lb"] = { 14, default = "kg lb" } }, link = "Stone (unit)", }, ["t"] = { name1 = "tonne", name1_us = "metric ton", symbol = "t", utype = "mass", scale = 1000, default = "LT ST", }, ["tonne"] = { name1 = "tonne", name1_us = "metric ton", symbol = "t", utype = "mass", scale = 1000, default = "shton", }, ["troy pound"] = { symbol = "troy pound", usename = 1, utype = "mass", scale = 0.3732417216, default = "lb kg", link = "Troy weight", }, ["usgalh2o"] = { name1 = "US gallon of water", name1_us = "U.S. gallon of water", name2 = "US gallons of water", name2_us = "U.S. gallons of water", symbol = "US&nbsp;gal H<sub>2</sub>O", utype = "mass", scale = 3.7776215836051126, default = "lb kg", link = "United States customary units#Fluid volume", }, ["viss"] = { name2 = "viss", symbol = "viss", utype = "mass", scale = 1.632932532, default = "kg", link = "Myanmar units of measurement#Mass", }, ["billion tonne"] = { target = "e9t", }, ["kilogram"] = { target = "kg", }, ["kilotonne"] = { target = "kt", }, ["lbs"] = { target = "lb", }, ["lbt"] = { target = "troy pound", }, ["lcwt"] = { target = "Lcwt", }, ["long ton"] = { target = "LT", }, ["mcg"] = { target = "μg", }, ["million tonne"] = { target = "e6t", }, ["scwt"] = { target = "Scwt", }, ["short ton"] = { target = "ST", }, ["stone"] = { target = "st", }, ["thousand tonne"] = { target = "e3t", }, ["tonnes"] = { target = "t", }, ["kg/kW"] = { name1 = "kilogram per kilowatt", name2 = "kilograms per kilowatt", symbol = "kg/kW", utype = "mass per unit power", scale = 0.001, default = "lb/hp", link = "Kilowatt", }, ["lb/hp"] = { name1 = "pound per horsepower", name2 = "pounds per horsepower", symbol = "lb/hp", utype = "mass per unit power", scale = 0.00060827738784176115, default = "kg/kW", link = "Horsepower", }, ["kg/h"] = { per = { "kg", "h" }, utype = "mass per unit time", default = "lb/h", }, ["lb/h"] = { per = { "lb", "h" }, utype = "mass per unit time", default = "kg/h", }, ["g-mol/d"] = { name1 = "gram-mole per day", name2 = "gram-moles per day", symbol = "g&#8209;mol/d", utype = "molar rate", scale = 1.1574074074074073e-5, default = "μmol/s", link = "Mole (unit)", }, ["g-mol/h"] = { name1 = "gram-mole per hour", name2 = "gram-moles per hour", symbol = "g&#8209;mol/h", utype = "molar rate", scale = 0.00027777777777777778, default = "mmol/s", link = "Mole (unit)", }, ["g-mol/min"] = { name1 = "gram-mole per minute", name2 = "gram-moles per minute", symbol = "g&#8209;mol/min", utype = "molar rate", scale = 0.016666666666666666, default = "g-mol/s", link = "Mole (unit)", }, ["g-mol/s"] = { name1 = "gram-mole per second", name2 = "gram-moles per second", symbol = "g&#8209;mol/s", utype = "molar rate", scale = 1, default = "lb-mol/min", link = "Mole (unit)", }, ["gmol/d"] = { name1 = "gram-mole per day", name2 = "gram-moles per day", symbol = "gmol/d", utype = "molar rate", scale = 1.1574074074074073e-5, default = "μmol/s", link = "Mole (unit)", }, ["gmol/h"] = { name1 = "gram-mole per hour", name2 = "gram-moles per hour", symbol = "gmol/h", utype = "molar rate", scale = 0.00027777777777777778, default = "mmol/s", link = "Mole (unit)", }, ["gmol/min"] = { name1 = "gram-mole per minute", name2 = "gram-moles per minute", symbol = "gmol/min", utype = "molar rate", scale = 0.016666666666666666, default = "gmol/s", link = "Mole (unit)", }, ["gmol/s"] = { name1 = "gram-mole per second", name2 = "gram-moles per second", symbol = "gmol/s", utype = "molar rate", scale = 1, default = "lbmol/min", link = "Mole (unit)", }, ["kmol/d"] = { name1 = "kilomole per day", name2 = "kilomoles per day", symbol = "kmol/d", utype = "molar rate", scale = 0.011574074074074073, default = "mmol/s", link = "Mole (unit)", }, ["kmol/h"] = { name1 = "kilomole per hour", name2 = "kilomoles per hour", symbol = "kmol/h", utype = "molar rate", scale = 0.27777777777777779, default = "mol/s", link = "Mole (unit)", }, ["kmol/min"] = { name1 = "kilomole per minute", name2 = "kilomoles per minute", symbol = "kmol/min", utype = "molar rate", scale = 16.666666666666668, default = "mol/s", link = "Kilomole (unit)", }, ["kmol/s"] = { name1 = "kilomole per second", name2 = "kilomoles per second", symbol = "kmol/s", utype = "molar rate", scale = 1000, default = "lb-mol/s", link = "Mole (unit)", }, ["lb-mol/d"] = { name1 = "pound-mole per day", name2 = "pound-moles per day", symbol = "lb&#8209;mol/d", utype = "molar rate", scale = 0.0052499116898148141, default = "mmol/s", link = "Pound-mole", }, ["lb-mol/h"] = { name1 = "pound-mole per hour", name2 = "pound-moles per hour", symbol = "lb&#8209;mol/h", utype = "molar rate", scale = 0.12599788055555555, default = "mol/s", link = "Pound-mole", }, ["lb-mol/min"] = { name1 = "pound-mole per minute", name2 = "pound-moles per minute", symbol = "lb&#8209;mol/min", utype = "molar rate", scale = 7.5598728333333334, default = "mol/s", link = "Pound-mole", }, ["lb-mol/s"] = { name1 = "pound-mole per second", name2 = "pound-moles per second", symbol = "lb&#8209;mol/s", utype = "molar rate", scale = 453.59237, default = "kmol/s", link = "Pound-mole", }, ["lbmol/d"] = { name1 = "pound-mole per day", name2 = "pound-moles per day", symbol = "lbmol/d", utype = "molar rate", scale = 0.0052499116898148141, default = "mmol/s", link = "Pound-mole", }, ["lbmol/h"] = { name1 = "pound-mole per hour", name2 = "pound-moles per hour", symbol = "lbmol/h", utype = "molar rate", scale = 0.12599788055555555, default = "mol/s", link = "Pound-mole", }, ["lbmol/min"] = { name1 = "pound-mole per minute", name2 = "pound-moles per minute", symbol = "lbmol/min", utype = "molar rate", scale = 7.5598728333333334, default = "mol/s", link = "Pound-mole", }, ["lbmol/s"] = { name1 = "pound-mole per second", name2 = "pound-moles per second", symbol = "lbmol/s", utype = "molar rate", scale = 453.59237, default = "kmol/s", link = "Pound-mole", }, ["mmol/s"] = { name1 = "millimole per second", name2 = "millimoles per second", symbol = "mmol/s", utype = "molar rate", scale = 0.001, default = "lb-mol/d", link = "Mole (unit)", }, ["mol/d"] = { name1 = "mole per day", name2 = "moles per day", symbol = "mol/d", utype = "molar rate", scale = 1.1574074074074073e-5, default = "μmol/s", link = "Mole (unit)", }, ["mol/h"] = { name1 = "mole per hour", name2 = "moles per hour", symbol = "mol/h", utype = "molar rate", scale = 0.00027777777777777778, default = "mmol/s", link = "Mole (unit)", }, ["mol/min"] = { name1 = "mole per minute", name2 = "moles per minute", symbol = "mol/min", utype = "molar rate", scale = 0.016666666666666666, default = "mol/s", link = "Mole (unit)", }, ["mol/s"] = { name1 = "mole per second", name2 = "moles per second", symbol = "mol/s", utype = "molar rate", scale = 1, default = "lb-mol/min", link = "Mole (unit)", }, ["μmol/s"] = { name1 = "micromole per second", name2 = "micromoles per second", symbol = "μmol/s", utype = "molar rate", scale = 0.000001, default = "lb-mol/d", link = "Mole (unit)", }, ["umol/s"] = { target = "μmol/s", }, ["/acre"] = { name1 = "per acre", name2 = "per acre", symbol = "/acre", utype = "per unit area", scale = 0.00024710538146716532, default = "/ha", link = "Acre", }, ["/ha"] = { name1 = "per hectare", name2 = "per hectare", symbol = "/ha", utype = "per unit area", scale = 100e-6, default = "/acre", link = "Hectare", }, ["/sqcm"] = { name1 = "per square centimetre", name1_us = "per square centimeter", name2 = "per square centimetre", name2_us = "per square centimeter", symbol = "/cm<sup>2</sup>", utype = "per unit area", scale = 1e4, default = "/sqin", link = "Square centimetre", }, ["/sqin"] = { name1 = "per square inch", name2 = "per square inch", symbol = "/in<sup>2</sup>", utype = "per unit area", scale = 1550.0031000062002, default = "/sqcm", link = "Square inch", }, ["/sqkm"] = { name1 = "per square kilometre", name1_us = "per square kilometer", name2 = "per square kilometre", name2_us = "per square kilometer", symbol = "/km<sup>2</sup>", utype = "per unit area", scale = 1e-6, default = "/sqmi", link = "Square kilometre", }, ["/sqmi"] = { name1 = "per square mile", name2 = "per square mile", symbol = "/sq&nbsp;mi", utype = "per unit area", scale = 3.8610215854244582e-7, default = "/sqkm", link = "Square mile", }, ["PD/acre"] = { name1 = "inhabitant per acre", name2 = "inhabitants per acre", symbol = "/acre", utype = "per unit area", scale = 0.00024710538146716532, default = "PD/ha", link = "Acre", }, ["PD/ha"] = { name1 = "inhabitant per hectare", name2 = "inhabitants per hectare", symbol = "/ha", utype = "per unit area", scale = 100e-6, default = "PD/acre", link = "Hectare", }, ["PD/sqkm"] = { name1 = "inhabitant per square kilometre", name1_us = "inhabitant per square kilometer", name2 = "inhabitants per square kilometre", name2_us = "inhabitants per square kilometer", symbol = "/km<sup>2</sup>", utype = "per unit area", scale = 1e-6, default = "PD/sqmi", link = "Square kilometre", }, ["PD/sqmi"] = { name1 = "inhabitant per square mile", name2 = "inhabitants per square mile", symbol = "/sq&nbsp;mi", utype = "per unit area", scale = 3.8610215854244582e-7, default = "PD/sqkm", link = "Square mile", }, ["/cm2"] = { target = "/sqcm", }, ["/in2"] = { target = "/sqin", }, ["/km2"] = { target = "/sqkm", }, ["pd/acre"] = { target = "PD/acre", }, ["pd/ha"] = { target = "PD/ha", }, ["PD/km2"] = { target = "PD/sqkm", }, ["pd/km2"] = { target = "PD/sqkm", }, ["PD/km²"] = { target = "PD/sqkm", }, ["pd/sqkm"] = { target = "PD/sqkm", }, ["pd/sqmi"] = { target = "PD/sqmi", }, ["/l"] = { name1 = "per litre", name1_us = "per liter", name2 = "per litre", name2_us = "per liter", symbol = "/l", utype = "per unit volume", scale = 1000, default = "/usgal", link = "Litre", }, ["/L"] = { name1 = "per litre", name1_us = "per liter", name2 = "per litre", name2_us = "per liter", symbol = "/L", utype = "per unit volume", scale = 1000, default = "/usgal", link = "Litre", }, ["/USgal"] = { name1 = "per gallon", name2 = "per gallon", symbol = "/gal", utype = "per unit volume", scale = 264.172052, default = "/L", link = "US gallon", customary= 2, }, ["/usgal"] = { target = "/USgal", }, ["bhp"] = { name1 = "brake horsepower", name2 = "brake horsepower", symbol = "bhp", utype = "power", scale = 745.69987158227022, default = "kW", link = "Horsepower#Brake horsepower", }, ["Cal/d"] = { name1 = "large calorie per day", name2 = "large calories per day", symbol = "Cal/d", utype = "power", scale = 0.048425925925925928, default = "kJ/d", link = "Calorie", }, ["Cal/h"] = { name1 = "large calorie per hour", name2 = "large calories per hour", symbol = "Cal/h", utype = "power", scale = 1.1622222222222223, default = "kJ/h", link = "Calorie", }, ["cal/h"] = { name1 = "calorie per hour", name2 = "calories per hour", symbol = "cal/h", utype = "power", scale = 0.0011622222222222223, default = "W", link = "Calorie", }, ["CV"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "CV", utype = "power", scale = 735.49875, default = "kW", }, ["hk"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "hk", utype = "power", scale = 735.49875, default = "kW", }, ["hp"] = { name1 = "horsepower", name2 = "horsepower", symbol = "hp", utype = "power", scale = 745.69987158227022, default = "kW", }, ["hp-electric"] = { name1 = "electric horsepower", name2 = "electric horsepower", symbol = "hp", utype = "power", scale = 746, default = "kW", link = "Horsepower#Electrical horsepower", }, ["hp-electrical"] = { name1 = "electrical horsepower", name2 = "electrical horsepower", symbol = "hp", utype = "power", scale = 746, default = "kW", link = "Horsepower#Electrical horsepower", }, ["hp-metric"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "hp", utype = "power", scale = 735.49875, default = "kW", }, ["ihp"] = { name1 = "indicated horsepower", name2 = "indicated horsepower", symbol = "ihp", utype = "power", scale = 745.69987158227022, default = "kW", link = "Horsepower#Indicated horsepower", }, ["kcal/h"] = { name1 = "kilocalorie per hour", name2 = "kilocalories per hour", symbol = "kcal/h", utype = "power", scale = 1.1622222222222223, default = "kW", link = "Calorie", }, ["kJ/d"] = { name1 = "kilojoule per day", name2 = "kilojoules per day", symbol = "kJ/d", utype = "power", scale = 0.011574074074074073, default = "Cal/d", link = "Kilojoule", }, ["kJ/h"] = { name1 = "kilojoule per hour", name2 = "kilojoules per hour", symbol = "kJ/h", utype = "power", scale = 0.27777777777777779, default = "W", link = "Kilojoule", }, ["PS"] = { name1 = "metric horsepower", name2 = "metric horsepower", symbol = "PS", utype = "power", scale = 735.49875, default = "kW", }, ["shp"] = { name1 = "shaft horsepower", name2 = "shaft horsepower", symbol = "shp", utype = "power", scale = 745.69987158227022, default = "kW", link = "Horsepower#Shaft horsepower", }, ["W"] = { _name1 = "watt", _symbol = "W", utype = "power", scale = 1, prefixes = 1, default = "hp", link = "Watt", }, ["BTU/h"] = { per = { "BTU", "h" }, utype = "power", default = "W", }, ["Btu/h"] = { per = { "Btu", "h" }, utype = "power", default = "W", }, ["BHP"] = { target = "bhp", }, ["btu/h"] = { target = "BTU/h", }, ["HP"] = { target = "hp", }, ["Hp"] = { target = "hp", }, ["hp-mechanical"] = { target = "hp", }, ["IHP"] = { target = "ihp", }, ["SHP"] = { target = "shp", }, ["whp"] = { target = "hp", }, ["hp/lb"] = { name1 = "horsepower per pound", name2 = "horsepower per pound", symbol = "hp/lb", utype = "power per unit mass", scale = 1643.986806, default = "kW/kg", link = "Power-to-weight ratio", }, ["hp/LT"] = { name1 = "horsepower per long ton", name2 = "horsepower per long ton", symbol = "hp/LT", utype = "power per unit mass", scale = 0.73392268125000004, default = "kW/t", link = "Power-to-weight ratio", }, ["hp/ST"] = { name1 = "horsepower per short ton", name2 = "horsepower per short ton", symbol = "hp/ST", utype = "power per unit mass", scale = 0.821993403, default = "kW/t", link = "Power-to-weight ratio", }, ["hp/t"] = { name1 = "horsepower per tonne", name2 = "horsepower per tonne", symbol = "hp/t", utype = "power per unit mass", scale = 0.74569987158227022, default = "kW/t", link = "Power-to-weight ratio", }, ["kW/kg"] = { name1 = "kilowatt per kilogram", name2 = "kilowatts per kilogram", symbol = "kW/kg", utype = "power per unit mass", scale = 1000, default = "hp/lb", link = "Power-to-weight ratio", }, ["kW/t"] = { name1 = "kilowatt per tonne", name2 = "kilowatts per tonne", symbol = "kW/t", utype = "power per unit mass", scale = 1, default = "PS/t", link = "Power-to-weight ratio", }, ["PS/t"] = { name1 = "metric horsepower per tonne", name2 = "metric horsepower per tonne", symbol = "PS/t", utype = "power per unit mass", scale = 0.73549875, default = "kW/t", link = "Power-to-weight ratio", }, ["shp/lb"] = { name1 = "shaft horsepower per pound", name2 = "shaft horsepower per pound", symbol = "shp/lb", utype = "power per unit mass", scale = 1643.986806, default = "kW/kg", link = "Power-to-weight ratio", }, ["hp/tonne"] = { target = "hp/t", symbol = "hp/tonne", default = "kW/tonne", }, ["kW/tonne"] = { target = "kW/t", symbol = "kW/tonne", }, ["-lb/in2"] = { name1 = "pound per square inch", name2 = "pounds per square inch", symbol = "lb/in<sup>2</sup>", utype = "pressure", scale = 6894.7572931683608, default = "kPa kgf/cm2", }, ["atm"] = { name1 = "standard atmosphere", symbol = "atm", utype = "pressure", scale = 101325, default = "kPa", link = "Atmosphere (unit)", }, ["Ba"] = { name1 = "barye", symbol = "Ba", utype = "pressure", scale = 0.1, default = "Pa", }, ["bar"] = { symbol = "bar", utype = "pressure", scale = 100000, default = "kPa", link = "Bar (unit)", }, ["dbar"] = { name1 = "decibar", symbol = "dbar", utype = "pressure", scale = 10000, default = "kPa", link = "Bar (unit)", }, ["inHg"] = { name1 = "inch of mercury", name2 = "inches of mercury", symbol = "inHg", utype = "pressure", scale = 3386.388640341, default = "kPa", }, ["kBa"] = { name1 = "kilobarye", symbol = "kBa", utype = "pressure", scale = 100, default = "hPa", link = "Barye", }, ["kg-f/cm2"] = { name1 = "kilogram-force per square centimetre", name1_us = "kilogram-force per square centimeter", name2 = "kilograms-force per square centimetre", name2_us = "kilograms-force per square centimeter", symbol = "kg<sub>f</sub>/cm<sup>2</sup>", utype = "pressure", scale = 98066.5, default = "psi", link = "Kilogram-force", }, ["kg/cm2"] = { name1 = "kilogram per square centimetre", name1_us = "kilogram per square centimeter", name2 = "kilograms per square centimetre", name2_us = "kilograms per square centimeter", symbol = "kg/cm<sup>2</sup>", utype = "pressure", scale = 98066.5, default = "psi", link = "Kilogram-force", }, ["kgf/cm2"] = { name1 = "kilogram-force per square centimetre", name1_us = "kilogram-force per square centimeter", name2 = "kilograms-force per square centimetre", name2_us = "kilograms-force per square centimeter", symbol = "kgf/cm<sup>2</sup>", utype = "pressure", scale = 98066.5, default = "psi", link = "Kilogram-force", }, ["ksi"] = { name1 = "kilopound per square inch", name2 = "kilopounds per square inch", symbol = "ksi", utype = "pressure", scale = 6894757.2931683613, default = "MPa", link = "Pound per square inch", }, ["lbf/in2"] = { name1 = "pound-force per square inch", name2 = "pounds-force per square inch", symbol = "lbf/in<sup>2</sup>", utype = "pressure", scale = 6894.7572931683608, default = "kPa kgf/cm2", }, ["mb"] = { name1 = "millibar", symbol = "mb", utype = "pressure", scale = 100, default = "hPa", link = "Bar (unit)", }, ["mbar"] = { name1 = "millibar", symbol = "mbar", utype = "pressure", scale = 100, default = "hPa", link = "Bar (unit)", }, ["mmHg"] = { name1 = "millimetre of mercury", name1_us = "millimeter of mercury", name2 = "millimetres of mercury", name2_us = "millimeters of mercury", symbol = "mmHg", utype = "pressure", scale = 133.322387415, default = "kPa", }, ["Pa"] = { _name1 = "pascal", _symbol = "Pa", utype = "pressure", scale = 1, prefixes = 1, default = "psi", link = "Pascal (unit)", }, ["psf"] = { name1 = "pound per square foot", name2 = "pounds per square foot", symbol = "psf", utype = "pressure", scale = 47.880258980335839, default = "kPa", link = "Pound per square inch", }, ["psi"] = { name1 = "pound per square inch", name2 = "pounds per square inch", symbol = "psi", utype = "pressure", scale = 6894.7572931683608, default = "kPa", }, ["Torr"] = { name1 = "torr", symbol = "Torr", utype = "pressure", scale = 133.32236842105263, default = "kPa", }, ["N/cm2"] = { per = { "N", "cm2" }, utype = "pressure", default = "psi", }, ["N/m2"] = { per = { "N", "m2" }, utype = "pressure", default = "psi", }, ["g/cm2"] = { per = { "g", "cm2" }, utype = "pressure", default = "lb/sqft", multiplier= 9.80665, }, ["g/m2"] = { per = { "g", "m2" }, utype = "pressure", default = "lb/sqft", multiplier= 9.80665, }, ["kg/ha"] = { per = { "kg", "ha" }, utype = "pressure", default = "lb/acre", multiplier= 9.80665, }, ["kg/m2"] = { per = { "kg", "m2" }, utype = "pressure", default = "lb/sqft", multiplier= 9.80665, }, ["lb/1000sqft"] = { per = { "lb", "1000sqft" }, utype = "pressure", default = "g/m2", multiplier= 9.80665, }, ["lb/acre"] = { per = { "lb", "acre" }, utype = "pressure", default = "kg/ha", multiplier= 9.80665, }, ["lb/sqft"] = { per = { "lb", "sqft" }, utype = "pressure", default = "kg/m2", multiplier= 9.80665, }, ["lb/sqyd"] = { per = { "lb", "sqyd" }, utype = "pressure", default = "kg/m2", multiplier= 9.80665, }, ["LT/acre"] = { per = { "LT", "acre" }, utype = "pressure", default = "t/ha", multiplier= 9.80665, }, ["MT/ha"] = { per = { "MT", "ha" }, utype = "pressure", default = "LT/acre ST/acre", multiplier= 9.80665, }, ["oz/sqft"] = { per = { "oz", "sqft" }, utype = "pressure", default = "g/m2", multiplier= 9.80665, }, ["oz/sqyd"] = { per = { "oz", "sqyd" }, utype = "pressure", default = "g/m2", multiplier= 9.80665, }, ["ST/acre"] = { per = { "ST", "acre" }, utype = "pressure", default = "t/ha", multiplier= 9.80665, }, ["t/ha"] = { per = { "t", "ha" }, utype = "pressure", default = "LT/acre ST/acre", multiplier= 9.80665, }, ["tonne/acre"] = { per = { "tonne", "acre" }, utype = "pressure", default = "tonne/ha", multiplier= 9.80665, }, ["tonne/ha"] = { per = { "tonne", "ha" }, utype = "pressure", default = "tonne/acre", multiplier= 9.80665, }, ["kgfpsqcm"] = { target = "kgf/cm2", }, ["kgpsqcm"] = { target = "kg/cm2", }, ["kN/m2"] = { target = "kPa", }, ["lb/in2"] = { target = "lbf/in2", }, ["torr"] = { target = "Torr", }, ["Bq"] = { _name1 = "becquerel", _symbol = "Bq", utype = "radioactivity", scale = 1, prefixes = 1, default = "pCi", link = "Becquerel", }, ["Ci"] = { _name1 = "curie", _symbol = "Ci", utype = "radioactivity", scale = 3.7e10, prefixes = 1, default = "GBq", link = "Curie (unit)", }, ["Rd"] = { _name1 = "rutherford", _symbol = "Rd", utype = "radioactivity", scale = 1e6, prefixes = 1, default = "MBq", link = "Rutherford (unit)", }, ["cm/h"] = { name1 = "centimetre per hour", name1_us = "centimeter per hour", name2 = "centimetres per hour", name2_us = "centimeters per hour", symbol = "cm/h", utype = "speed", scale = 2.7777777777777775e-6, default = "in/h", link = "Metre per second", }, ["cm/s"] = { name1 = "centimetre per second", name1_us = "centimeter per second", name2 = "centimetres per second", name2_us = "centimeters per second", symbol = "cm/s", utype = "speed", scale = 0.01, default = "in/s", link = "Metre per second", }, ["cm/year"] = { name1 = "centimetre per year", name1_us = "centimeter per year", name2 = "centimetres per year", name2_us = "centimeters per year", symbol = "cm/year", utype = "speed", scale = 3.168873850681143e-10, default = "in/year", link = "Orders of magnitude (speed)", }, ["foot/s"] = { name1 = "foot per second", name2 = "foot per second", symbol = "ft/s", utype = "speed", scale = 0.3048, default = "m/s", }, ["ft/min"] = { name1 = "foot per minute", name2 = "feet per minute", symbol = "ft/min", utype = "speed", scale = 0.00508, default = "m/min", link = "Feet per second", }, ["ft/s"] = { name1 = "foot per second", name2 = "feet per second", symbol = "ft/s", utype = "speed", scale = 0.3048, default = "m/s", link = "Feet per second", }, ["furlong per fortnight"] = { name2 = "furlongs per fortnight", symbol = "furlong per fortnight", usename = 1, utype = "speed", scale = 0.00016630952380952381, default = "km/h mph", link = "FFF system", }, ["in/h"] = { name1 = "inch per hour", name2 = "inches per hour", symbol = "in/h", utype = "speed", scale = 7.0555555555555559e-6, default = "cm/h", link = "Inch", }, ["in/s"] = { name1 = "inch per second", name2 = "inches per second", symbol = "in/s", utype = "speed", scale = 0.0254, default = "cm/s", link = "Inch", }, ["in/year"] = { name1 = "inch per year", name2 = "inches per year", symbol = "in/year", utype = "speed", scale = 8.0489395807301024e-10, default = "cm/year", link = "Orders of magnitude (speed)", }, ["isp"] = { name1 = "second", symbol = "s", utype = "speed", scale = 9.80665, default = "km/s", link = "Specific impulse", }, ["km/d"] = { name1 = "kilometre per day", name1_us = "kilometer per day", name2 = "kilometres per day", name2_us = "kilometers per day", symbol = "km/d", utype = "speed", scale = 1.1574074074074074e-2, default = "mi/d", link = "Orders of magnitude (speed)", }, ["km/h"] = { name1 = "kilometre per hour", name1_us = "kilometer per hour", name2 = "kilometres per hour", name2_us = "kilometers per hour", symbol = "km/h", utype = "speed", scale = 0.27777777777777779, default = "mph", link = "Kilometres per hour", }, ["km/s"] = { name1 = "kilometre per second", name1_us = "kilometer per second", name2 = "kilometres per second", name2_us = "kilometers per second", symbol = "km/s", utype = "speed", scale = 1000, default = "mi/s", link = "Metre per second", }, ["kn"] = { name1 = "knot", symbol = "kn", utype = "speed", scale = 0.51444444444444448, default = "km/h mph", link = "Knot (unit)", }, ["kNs/kg"] = { name2 = "kN&#8209;s/kg", symbol = "kN&#8209;s/kg", utype = "speed", scale = 1000, default = "isp", link = "Specific impulse", }, ["m/min"] = { name1 = "metre per minute", name1_us = "meter per minute", name2 = "metres per minute", name2_us = "meters per minute", symbol = "m/min", utype = "speed", scale = 0.016666666666666666, default = "ft/min", link = "Metre per second", }, ["m/s"] = { name1 = "metre per second", name1_us = "meter per second", name2 = "metres per second", name2_us = "meters per second", symbol = "m/s", utype = "speed", scale = 1, default = "ft/s", }, ["Mach"] = { name2 = "Mach", symbol = "Mach", utype = "speed", builtin = "mach", scale = 0, iscomplex= true, default = "km/h mph", link = "Mach number", }, ["mi/d"] = { name1 = "mile per day", name2 = "miles per day", symbol = "mi/d", utype = "speed", scale = 1.8626666666666667e-2, default = "km/d", link = "Orders of magnitude (speed)", }, ["mi/s"] = { name1 = "mile per second", name2 = "miles per second", symbol = "mi/s", utype = "speed", scale = 1609.344, default = "km/s", link = "Mile", }, ["mm/h"] = { name1 = "millimetre per hour", name1_us = "millimeter per hour", name2 = "millimetres per hour", name2_us = "millimeters per hour", symbol = "mm/h", utype = "speed", scale = 2.7777777777777781e-7, default = "in/h", link = "Metre per second", }, ["mph"] = { name1 = "mile per hour", name2 = "miles per hour", symbol = "mph", utype = "speed", scale = 0.44704, default = "km/h", link = "Miles per hour", }, ["Ns/kg"] = { name2 = "N&#8209;s/kg", symbol = "N&#8209;s/kg", utype = "speed", scale = 1, default = "isp", link = "Specific impulse", }, ["si tsfc"] = { name2 = "g/(kN⋅s)", symbol = "g/(kN⋅s)", utype = "speed", scale = 9.9999628621379242e-7, invert = -1, iscomplex= true, default = "tsfc", link = "Thrust specific fuel consumption", }, ["tsfc"] = { name2 = "lb/(lbf⋅h)", symbol = "lb/(lbf⋅h)", utype = "speed", scale = 2.832545036049801e-5, invert = -1, iscomplex= true, default = "si tsfc", link = "Thrust specific fuel consumption", }, ["cm/y"] = { target = "cm/year", }, ["cm/yr"] = { target = "cm/year", }, ["in/y"] = { target = "in/year", }, ["in/yr"] = { target = "in/year", }, ["knot"] = { target = "kn", }, ["knots"] = { target = "kn", }, ["kph"] = { target = "km/h", }, ["mi/h"] = { target = "mph", }, ["mm/s"] = { per = { "mm", "s" }, utype = "speed", default = "in/s", link = "Metre per second", }, ["C"] = { name1 = "degree Celsius", name2 = "degrees Celsius", symbol = "°C", usesymbol= 1, utype = "temperature", scale = 1, offset = -273.15, iscomplex= true, istemperature= true, default = "F", link = "Celsius", }, ["F"] = { name1 = "degree Fahrenheit", name2 = "degrees Fahrenheit", symbol = "°F", usesymbol= 1, utype = "temperature", scale = 0.55555555555555558, offset = 32-273.15*(9/5), iscomplex= true, istemperature= true, default = "C", link = "Fahrenheit", }, ["K"] = { _name1 = "kelvin", _symbol = "K", usesymbol= 1, utype = "temperature", scale = 1, offset = 0, iscomplex= true, istemperature= true, prefixes = 1, default = "C F", link = "Kelvin", }, ["keVT"] = { name1 = "kiloelectronvolt", symbol = "keV", utype = "temperature", scale = 11.604505e6, offset = 0, iscomplex= true, default = "MK", link = "Electronvolt", }, ["R"] = { name1 = "degree Rankine", name2 = "degrees Rankine", symbol = "°R", usesymbol= 1, utype = "temperature", scale = 0.55555555555555558, offset = 0, iscomplex= true, istemperature= true, default = "K F C", link = "Rankine scale", }, ["Celsius"] = { target = "C", }, ["°C"] = { target = "C", }, ["°F"] = { target = "F", }, ["°R"] = { target = "R", }, ["C-change"] = { name1 = "degree Celsius change", name2 = "degrees Celsius change", symbol = "°C", usesymbol= 1, utype = "temperature change", scale = 1, default = "F-change", link = "Celsius", }, ["F-change"] = { name1 = "degree Fahrenheit change", name2 = "degrees Fahrenheit change", symbol = "°F", usesymbol= 1, utype = "temperature change", scale = 0.55555555555555558, default = "C-change", link = "Fahrenheit", }, ["K-change"] = { name1 = "kelvin change", name2 = "kelvins change", symbol = "K", usesymbol= 1, utype = "temperature change", scale = 1, default = "F-change", link = "Kelvin", }, ["°C-change"] = { target = "C-change", }, ["°F-change"] = { target = "F-change", }, ["century"] = { name1 = "century", name2 = "centuries", symbol = "ha", utype = "time", scale = 3155760000, default = "Gs", }, ["d"] = { name1 = "day", symbol = "d", utype = "time", scale = 86400, default = "ks", }, ["decade"] = { name1 = "decade", symbol = "daa", utype = "time", scale = 315576000, default = "Ms", }, ["dog year"] = { name1 = "dog year", symbol = "dog yr", utype = "time", scale = 220903200, default = "years", link = "List of unusual units of measurement#Dog year", }, ["fortnight"] = { symbol = "fortnight", usename = 1, utype = "time", scale = 1209600, default = "week", }, ["h"] = { name1 = "hour", symbol = "h", utype = "time", scale = 3600, default = "ks", }, ["long billion year"] = { name1 = "billion years", name2 = "billion years", symbol = "Ta", utype = "time", scale = 31557600000000000000, default = "Es", link = "Annum", }, ["millennium"] = { name1 = "millennium", name2 = "millennia", symbol = "ka", utype = "time", scale = 31557600000, default = "Gs", }, ["milliard year"] = { name1 = "milliard years", name2 = "milliard years", symbol = "Ga", utype = "time", scale = 31557600000000000, default = "Ps", link = "Annum", }, ["million year"] = { name1 = "million years", name2 = "million years", symbol = "Ma", utype = "time", scale = 31557600000000, default = "Ts", link = "Annum", }, ["min"] = { name1 = "minute", symbol = "min", utype = "time", scale = 60, default = "s", }, ["month"] = { symbol = "month", usename = 1, utype = "time", scale = 2629800, default = "Ms", }, ["months"] = { name1 = "month", symbol = "mo", utype = "time", scale = 2629800, default = "year", }, ["s"] = { _name1 = "second", _symbol = "s", utype = "time", scale = 1, prefixes = 1, default = "v < 7200 ! min ! h", link = "Second", }, ["short billion year"] = { name1 = "billion years", name2 = "billion years", symbol = "Ga", utype = "time", scale = 31557600000000000, default = "Ps", link = "Annum", }, ["short trillion year"] = { name1 = "trillion years", name2 = "trillion years", symbol = "Ta", utype = "time", scale = 31557600000000000000, default = "Es", link = "Annum", }, ["thousand million year"] = { name1 = "thousand million years", name2 = "thousand million years", symbol = "Ga", utype = "time", scale = 31557600000000000, default = "Ps", link = "Annum", }, ["wk"] = { symbol = "week", usename = 1, utype = "time", scale = 604800, default = "Ms", }, ["year"] = { name1 = "year", symbol = "a", utype = "time", scale = 31557600, default = "Ms", link = "Annum", }, ["years"] = { name1 = "year", symbol = "yr", utype = "time", scale = 31557600, default = "Ms", link = "Annum", }, ["byr"] = { target = "short billion year", }, ["day"] = { target = "d", }, ["days"] = { target = "d", }, ["dog yr"] = { target = "dog year", }, ["Gyr"] = { target = "thousand million year", }, ["hour"] = { target = "h", }, ["hours"] = { target = "h", }, ["kMyr"] = { target = "thousand million year", }, ["kmyr"] = { target = "thousand million year", }, ["kyr"] = { target = "millennium", }, ["long byr"] = { target = "long billion year", }, ["minute"] = { target = "min", }, ["minutes"] = { target = "min", }, ["mth"] = { target = "month", }, ["Myr"] = { target = "million year", }, ["myr"] = { target = "million year", }, ["sec"] = { target = "s", }, ["second"] = { target = "s", }, ["seconds"] = { target = "s", }, ["tmyr"] = { target = "thousand million year", }, ["tryr"] = { target = "short trillion year", }, ["tyr"] = { target = "millennium", }, ["week"] = { target = "wk", }, ["weeks"] = { target = "wk", }, ["yr"] = { target = "year", }, ["kg.m"] = { name1 = "kilogram metre", name1_us = "kilogram meter", symbol = "kg⋅m", utype = "torque", scale = 9.80665, default = "Nm lbft", link = "Kilogram metre (torque)", }, ["kgf.m"] = { name1 = "kilogram force-metre", name1_us = "kilogram force-meter", symbol = "kgf⋅m", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["kgm"] = { name1 = "kilogram metre", name1_us = "kilogram meter", symbol = "kg⋅m", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["kpm"] = { name1 = "kilopond metre", name1_us = "kilopond meter", symbol = "kp⋅m", utype = "torque", scale = 9.80665, default = "Nm lbft", link = "Kilogram metre (torque)", }, ["lb-fft"] = { name1 = "pound force-foot", name2 = "pound force-feet", symbol = "ft⋅lb<sub>f</sub>", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["lb.ft"] = { name1 = "pound force-foot", name2 = "pound force-feet", symbol = "lb⋅ft", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["lb.in"] = { name1 = "pound force-inch", symbol = "lb⋅in", utype = "torque", scale = 0.1129848290276167, default = "mN.m", link = "Pound-foot (torque)", }, ["lbfft"] = { name1 = "pound force-foot", name2 = "pound force-feet", symbol = "lbf⋅ft", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["lbft"] = { name1 = "pound-foot", name2 = "pound-feet", symbol = "lb⋅ft", utype = "torque", scale = 1.3558179483314004, default = "Nm", link = "Pound-foot (torque)", }, ["m.kg-f"] = { name1 = "metre kilogram-force", name1_us = "meter kilogram-force", name2 = "metre kilograms-force", name2_us = "meter kilograms-force", symbol = "m⋅kg<sub>f</sub>", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["m.kgf"] = { name1 = "metre kilogram-force", name1_us = "meter kilogram-force", name2 = "metre kilograms-force", name2_us = "meter kilograms-force", symbol = "m⋅kgf", utype = "torque", scale = 9.80665, default = "Nm lbfft", link = "Kilogram metre (torque)", }, ["mN.m"] = { name1 = "millinewton-metre", name1_us = "millinewton-meter", symbol = "mN⋅m", utype = "torque", scale = 0.001, default = "lb.in", link = "Newton-metre", }, ["Nm"] = { _name1 = "newton-metre", _name1_us= "newton-meter", _symbol = "N⋅m", utype = "torque", alttype = "energy", scale = 1, prefixes = 1, default = "lbfft", link = "Newton-metre", }, ["kN/m"] = { per = { "kN", "-m-stiff" }, utype = "torque", default = "lbf/in", }, ["lbf/in"] = { per = { "lbf", "-in-stiff" }, utype = "torque", default = "kN/m", }, ["lb-f.ft"] = { target = "lb-fft", }, ["lbf.ft"] = { target = "lbfft", }, ["lbf·ft"] = { target = "lbfft", }, ["lb·ft"] = { target = "lb.ft", }, ["mkg-f"] = { target = "m.kg-f", }, ["mkgf"] = { target = "m.kgf", }, ["N.m"] = { target = "Nm", }, ["N·m"] = { target = "Nm", }, ["ton-mile"] = { symbol = "ton-mile", usename = 1, utype = "transportation", scale = 1.4599723182105602, default = "tkm", }, ["tkm"] = { name1 = "tonne-kilometre", name1_us = "tonne-kilometer", symbol = "tkm", utype = "transportation", scale = 1, default = "ton-mile", }, ["-12USoz(mL)serve"] = { name1_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;mL) serving", symbol = "12&nbsp;US&nbsp;fl&nbsp;oz (355&nbsp;mL) serving", sym_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;mL) serving", utype = "volume", scale = 0.00035488235475000004, default = "mL", link = "Beverage can#Standard sizes", }, ["-12USoz(ml)serve"] = { name1_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;ml) serving", symbol = "12&nbsp;US&nbsp;fl&nbsp;oz (355&nbsp;ml) serving", sym_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz (355&nbsp;ml) serving", utype = "volume", scale = 0.00035488235475000004, default = "ml", link = "Beverage can#Standard sizes", }, ["-12USozserve"] = { name1_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz serving", symbol = "12&nbsp;US&nbsp;fl&nbsp;oz serving", sym_us = "12&nbsp;U.S.&nbsp;fl&nbsp;oz serving", utype = "volume", scale = 0.00035488235475000004, default = "mL", link = "Beverage can#Standard sizes", }, ["acre-foot"] = { name1 = "acre-foot", name2 = "acre-foot", symbol = "acre⋅ft", utype = "volume", scale = 1233.48183754752, default = "m3", }, ["acre-ft"] = { name1 = "acre-foot", name2 = "acre-feet", symbol = "acre⋅ft", utype = "volume", scale = 1233.48183754752, default = "m3", }, ["AUtbsp"] = { name1 = "Australian tablespoon", symbol = "AU&nbsp;tbsp", utype = "volume", scale = 0.000020, default = "ml", }, ["Bcuft"] = { name1 = "billion cubic foot", name2 = "billion cubic feet", symbol = "billion cu&nbsp;ft", utype = "volume", scale = 28316846.592, default = "Gl", link = "Cubic foot", }, ["bdft"] = { name1 = "board foot", name2 = "board feet", symbol = "bd&nbsp;ft", utype = "volume", scale = 0.0023597372167, default = "m3", }, ["board feet"] = { name2 = "board feet", symbol = "board foot", usename = 1, utype = "volume", scale = 0.0023597372167, default = "m3", }, ["board foot"] = { name2 = "board foot", symbol = "board foot", usename = 1, utype = "volume", scale = 0.0023597372167, default = "m3", }, ["cc"] = { name1 = "cubic centimetre", name1_us = "cubic centimeter", symbol = "cc", utype = "volume", scale = 0.000001, default = "cuin", }, ["CID"] = { name1 = "cubic inch", name2 = "cubic inches", symbol = "cu&nbsp;in", utype = "volume", scale = 0.000016387064, default = "cc", link = "Cubic inch#Engine displacement", }, ["cord"] = { symbol = "cord", utype = "volume", scale = 3.624556363776, default = "m3", link = "Cord (unit)", }, ["cufoot"] = { name1 = "cubic foot", name2 = "cubic foot", symbol = "cu&nbsp;ft", utype = "volume", scale = 0.028316846592, default = "m3", }, ["cuft"] = { name1 = "cubic foot", name2 = "cubic feet", symbol = "cu&nbsp;ft", utype = "volume", scale = 0.028316846592, default = "m3", }, ["cuin"] = { name1 = "cubic inch", name2 = "cubic inches", symbol = "cu&nbsp;in", utype = "volume", scale = 0.000016387064, default = "cm3", }, ["cumi"] = { name1 = "cubic mile", symbol = "cu&nbsp;mi", utype = "volume", scale = 4168181825.440579584, default = "km3", }, ["cuyd"] = { name1 = "cubic yard", symbol = "cu&nbsp;yd", utype = "volume", scale = 0.764554857984, default = "m3", }, ["firkin"] = { symbol = "firkin", usename = 1, utype = "volume", scale = 0.04091481, default = "L impgal USgal", link = "Firkin (unit)", }, ["foot3"] = { target = "cufoot", }, ["Goilbbl"] = { name1 = "billion barrels", name2 = "billion barrels", symbol = "Gbbl", utype = "volume", scale = 158987294.928, default = "v * 1.58987294928 < 10 ! e6 ! e9 ! m3", link = "Barrel (unit)#Oil barrel", }, ["gr water"] = { name1 = "grains water", name2 = "grains water", symbol = "gr H<sub>2</sub>O", utype = "volume", scale = 0.00000006479891, default = "cm3", link = "Grain (unit)", }, ["grt"] = { name1 = "gross register ton", symbol = "grt", utype = "volume", scale = 2.8316846592, default = "m3", link = "Gross register tonnage", }, ["impbbl"] = { name1 = "imperial barrel", symbol = "imp&nbsp;bbl", utype = "volume", scale = 0.16365924, default = "L impgal USgal", link = "Barrel (unit)", }, ["impbsh"] = { name1 = "imperial bushel", symbol = "imp&nbsp;bsh", utype = "volume", scale = 0.03636872, default = "L impgal USdrygal", }, ["impbu"] = { name1 = "imperial bushel", symbol = "imp&nbsp;bu", utype = "volume", scale = 0.03636872, default = "m3", }, ["impgal"] = { name1 = "imperial gallon", symbol = "imp&nbsp;gal", utype = "volume", scale = 0.00454609, default = "L USgal", }, ["impgi"] = { name1 = "gill", symbol = "gi", utype = "volume", scale = 0.0001420653125, default = "ml USoz", link = "Gill (unit)", }, ["impkenning"] = { name1 = "imperial kenning", symbol = "kenning", utype = "volume", scale = 0.01818436, default = "L USdrygal", link = "Kenning (unit)", }, ["impoz"] = { name1 = "imperial fluid ounce", symbol = "imp&nbsp;fl&nbsp;oz", utype = "volume", scale = 0.0000284130625, default = "ml USoz", }, ["imppk"] = { name1 = "imperial peck", symbol = "pk", utype = "volume", scale = 0.00909218, default = "L USdrygal", link = "Peck", }, ["imppt"] = { name1 = "imperial pint", symbol = "imp&nbsp;pt", utype = "volume", scale = 0.00056826125, default = "L", }, ["impqt"] = { name1 = "imperial quart", symbol = "imp&nbsp;qt", utype = "volume", scale = 0.0011365225, default = "ml USoz", customary= 3, }, ["kilderkin"] = { symbol = "kilderkin", usename = 1, utype = "volume", scale = 0.08182962, default = "L impgal USgal", }, ["koilbbl"] = { name1 = "thousand barrels", name2 = "thousand barrels", symbol = "kbbl", utype = "volume", scale = 158.987294928, default = "v * 1.58987294928 < 10 ! ! e3 ! m3", link = "Barrel (unit)#Oil barrel", }, ["L"] = { _name1 = "litre", _name1_us= "liter", _symbol = "L", utype = "volume", scale = 0.001, prefixes = 1, default = "impgal USgal", link = "Litre", }, ["l"] = { _name1 = "litre", _name1_us= "liter", _symbol = "l", utype = "volume", scale = 0.001, prefixes = 1, default = "impgal USgal", link = "Litre", }, ["ll"] = { name1 = "litre", name1_us = "liter", symbol = "l", utype = "volume", scale = 0.001, default = "impgal USgal", }, ["m3"] = { _name1 = "cubic metre", _name1_us= "cubic meter", _symbol = "m<sup>3</sup>", prefix_position= 7, utype = "volume", scale = 1, prefixes = 3, default = "cuft", link = "Cubic metre", }, ["Mbbl"] = { name1 = "thousand barrels", name2 = "thousand barrels", symbol = "Mbbl", utype = "volume", scale = 158.987294928, default = "v * 1.58987294928 < 10 ! e3 ! ! m3", link = "Barrel (unit)#Oil barrel", }, ["MMoilbbl"] = { name1 = "million barrels", name2 = "million barrels", symbol = "MMbbl", utype = "volume", scale = 158987.294928, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3", link = "Barrel (unit)#Oil barrel", }, ["Moilbbl"] = { name1 = "million barrels", name2 = "million barrels", symbol = "Mbbl", utype = "volume", scale = 158987.294928, default = "v * 1.58987294928 < 10 ! e3 ! e6 ! m3", link = "Barrel (unit)#Oil barrel", }, ["MTON"] = { name1 = "measurement ton", symbol = "MTON", utype = "volume", scale = 1.13267386368, default = "m3", }, ["MUSgal"] = { name1 = "million US gallons", name1_us = "million U.S. gallons", name2 = "million US gallons", name2_us = "million U.S. gallons", symbol = "million US&nbsp;gal", sym_us = "million U.S.&nbsp;gal", utype = "volume", scale = 3785.411784, default = "Ml", link = "US gallon", }, ["oilbbl"] = { name1 = "barrel", symbol = "bbl", utype = "volume", scale = 0.158987294928, default = "m3", link = "Barrel (unit)#Oil barrel", }, ["stere"] = { symbol = "stere", usename = 1, utype = "volume", scale = 1, default = "cuft", }, ["Toilbbl"] = { name1 = "trillion barrels", name2 = "trillion barrels", symbol = "Tbbl", utype = "volume", scale = 158987294928, default = "v * 1.58987294928 < 10 ! e9 ! e12 ! m3", link = "Barrel (unit)#Oil barrel", }, ["USbbl"] = { name1 = "US barrel", name1_us = "U.S. barrel", symbol = "US&nbsp;bbl", sym_us = "U.S.&nbsp;bbl", utype = "volume", scale = 0.119240471196, default = "L USgal impgal", link = "Barrel (unit)", }, ["USbeerbbl"] = { name1 = "US beer barrel", name1_us = "U.S. beer barrel", symbol = "US&nbsp;bbl", sym_us = "U.S.&nbsp;bbl", utype = "volume", scale = 0.117347765304, default = "L USgal impgal", link = "Barrel (unit)", }, ["USbsh"] = { name1 = "US bushel", name1_us = "U.S. bushel", symbol = "US&nbsp;bsh", sym_us = "U.S.&nbsp;bsh", utype = "volume", scale = 0.03523907016688, default = "L USdrygal impgal", link = "Bushel", }, ["USbu"] = { name1 = "US bushel", name1_us = "U.S. bushel", symbol = "US&nbsp;bu", sym_us = "U.S.&nbsp;bu", utype = "volume", scale = 0.03523907016688, default = "L USdrygal impgal", link = "Bushel", }, ["USdrybbl"] = { name1 = "US dry barrel", name1_us = "U.S. dry barrel", symbol = "US&nbsp;dry&nbsp;bbl", sym_us = "U.S.&nbsp;dry&nbsp;bbl", utype = "volume", scale = 0.11562819898508, default = "m3", link = "Barrel (unit)", }, ["USdrygal"] = { name1 = "US dry gallon", name1_us = "U.S. dry gallon", symbol = "US&nbsp;dry&nbsp;gal", sym_us = "U.S.&nbsp;dry&nbsp;gal", utype = "volume", scale = 0.00440488377086, default = "L", link = "Gallon", }, ["USdrypt"] = { name1 = "US dry pint", name1_us = "U.S. dry pint", symbol = "US&nbsp;dry&nbsp;pt", sym_us = "U.S.&nbsp;dry&nbsp;pt", utype = "volume", scale = 0.0005506104713575, default = "ml", link = "Pint", }, ["USdryqt"] = { name1 = "US dry quart", name1_us = "U.S. dry quart", symbol = "US&nbsp;dry&nbsp;qt", sym_us = "U.S.&nbsp;dry&nbsp;qt", utype = "volume", scale = 0.001101220942715, default = "ml", link = "Quart", }, ["USflgal"] = { name1 = "US gallon", name1_us = "U.S. gallon", symbol = "US fl gal", sym_us = "U.S.&nbsp;fl&nbsp;gal", utype = "volume", scale = 0.003785411784, default = "L impgal", link = "Gallon", }, ["USgal"] = { name1 = "US gallon", name1_us = "U.S. gallon", symbol = "US&nbsp;gal", sym_us = "U.S.&nbsp;gal", utype = "volume", scale = 0.003785411784, default = "L impgal", }, ["USgi"] = { name1 = "gill", symbol = "gi", utype = "volume", scale = 0.0001182941183, default = "ml impoz", link = "Gill (unit)", }, ["USkenning"] = { name1 = "US kenning", name1_us = "U.S. kenning", symbol = "US&nbsp;kenning", sym_us = "U.S.&nbsp;kenning", utype = "volume", scale = 0.01761953508344, default = "L impgal", link = "Kenning (unit)", }, ["USmin"] = { name1 = "US minim", name1_us = "U.S. minim", symbol = "US&nbsp;min", sym_us = "U.S.&nbsp;min", utype = "volume", scale = 0.000000061611519921875, default = "ml", link = "Minim (unit)", }, ["USoz"] = { name1 = "US fluid ounce", name1_us = "U.S. fluid ounce", symbol = "US&nbsp;fl&nbsp;oz", sym_us = "U.S.&nbsp;fl&nbsp;oz", utype = "volume", scale = 0.0000295735295625, default = "ml", }, ["USpk"] = { name1 = "US peck", name1_us = "U.S. peck", symbol = "US&nbsp;pk", sym_us = "U.S.&nbsp;pk", utype = "volume", scale = 0.00880976754172, default = "L impgal", link = "Peck", }, ["USpt"] = { name1 = "US pint", name1_us = "U.S. pint", symbol = "US&nbsp;pt", sym_us = "U.S.&nbsp;pt", utype = "volume", scale = 0.000473176473, default = "L imppt", link = "Pint", }, ["USqt"] = { name1 = "US quart", name1_us = "U.S. quart", symbol = "US&nbsp;qt", sym_us = "U.S.&nbsp;qt", utype = "volume", scale = 0.000946352946, default = "ml", link = "Quart", customary= 1, }, ["USquart"] = { name1 = "US quart", name1_us = "U.S. quart", symbol = "US&nbsp;qt", sym_us = "U.S.&nbsp;qt", utype = "volume", scale = 0.000946352946, default = "ml impoz", link = "Quart", }, ["UStbsp"] = { name1 = "US tablespoon", name1_us = "U.S. tablespoon", symbol = "US&nbsp;tbsp", sym_us = "U.S.&nbsp;tbsp", utype = "volume", scale = 1.4786764781250001e-5, default = "ml", }, ["winecase"] = { symbol = "case", usename = 1, utype = "volume", scale = 0.009, default = "L", link = "Case (goods)", }, ["*U.S.drygal"] = { target = "USdrygal", sp_us = true, customary= 2, }, ["*U.S.gal"] = { target = "USgal", sp_us = true, customary= 2, }, ["+USdrygal"] = { target = "USdrygal", customary= 1, }, ["+usfloz"] = { target = "USoz", link = "Fluid ounce", customary= 1, }, ["+USgal"] = { target = "USgal", customary= 1, }, ["+USoz"] = { target = "USoz", customary= 1, }, ["@impgal"] = { target = "impgal", link = "Gallon", customary= 3, }, ["acre feet"] = { target = "acre-ft", }, ["acre foot"] = { target = "acre-foot", }, ["acre ft"] = { target = "acre-ft", }, ["acre-feet"] = { target = "acre-ft", }, ["acre.foot"] = { target = "acre-foot", }, ["acre.ft"] = { target = "acre-ft", }, ["acre·ft"] = { target = "acre-ft", }, ["bushels"] = { target = "USbsh", }, ["cid"] = { target = "CID", }, ["ft3"] = { target = "cuft", }, ["gal"] = { target = "USgal", }, ["gallon"] = { shouldbe = "Use %{USgal%} for US gallons or %{impgal%} for imperial gallons (not %{gallon%})", }, ["gallons"] = { shouldbe = "Use %{USgal%} for US gallons or %{impgal%} for imperial gallons (not %{gallons%})", }, ["Gcuft"] = { target = "e9cuft", }, ["impfloz"] = { target = "impoz", }, ["Impgal"] = { target = "impgal", }, ["in3"] = { target = "cuin", symbol = "in<sup>3</sup>", }, ["hm³"] = { target = "hm3", }, ["kcuft"] = { target = "e3cuft", }, ["kcum"] = { target = "e3m3", }, ["km³"] = { target = "km3", }, ["liter"] = { target = "L", sp_us = true, }, ["liters"] = { target = "L", sp_us = true, }, ["litre"] = { target = "L", }, ["litres"] = { target = "L", }, ["Mcuft"] = { target = "e6cuft", }, ["Mcum"] = { target = "e6m3", }, ["Mft3"] = { target = "e6cuft", }, ["mi3"] = { target = "cumi", }, ["m³"] = { target = "m3", }, ["Pcuft"] = { target = "e15cuft", }, ["pt"] = { shouldbe = "Use %{USpt%} for US pints or %{imppt%} for imperial pints (not %{pt%})", }, ["qt"] = { shouldbe = "Use %{USqt%} for US quarts or %{impqt%} for imperial quarts (not %{qt%})", }, ["Tcuft"] = { target = "e12cuft", }, ["Tft3"] = { target = "e12cuft", }, ["U.S.bbl"] = { target = "USbbl", sp_us = true, default = "L U.S.gal impgal", }, ["U.S.beerbbl"] = { target = "USbeerbbl", sp_us = true, default = "L U.S.gal impgal", }, ["U.S.bsh"] = { target = "USbsh", sp_us = true, default = "L U.S.drygal impgal", }, ["U.S.bu"] = { target = "USbu", sp_us = true, default = "L U.S.drygal impgal", }, ["U.S.drybbl"] = { target = "USdrybbl", sp_us = true, }, ["U.S.drygal"] = { target = "USdrygal", sp_us = true, }, ["U.S.drypt"] = { target = "USdrypt", sp_us = true, }, ["U.S.dryqt"] = { target = "USdryqt", sp_us = true, }, ["U.S.flgal"] = { target = "USflgal", sp_us = true, }, ["U.S.floz"] = { target = "USoz", sp_us = true, }, ["U.S.gal"] = { target = "USgal", sp_us = true, link = "U.S. gallon", }, ["u.s.gal"] = { target = "USgal", sp_us = true, link = "U.S. gallon", }, ["U.S.gi"] = { target = "USgi", sp_us = true, }, ["U.S.kenning"] = { target = "USkenning", sp_us = true, }, ["U.S.oz"] = { target = "USoz", sp_us = true, }, ["U.S.pk"] = { target = "USpk", sp_us = true, }, ["U.S.pt"] = { target = "USpt", sp_us = true, }, ["U.S.qt"] = { target = "USqt", sp_us = true, default = "L impqt", customary= 2, }, ["usbbl"] = { target = "USbbl", }, ["usbeerbbl"] = { target = "USbeerbbl", }, ["usbsh"] = { target = "USbsh", }, ["usbu"] = { target = "USbu", }, ["usdrybbl"] = { target = "USdrybbl", }, ["usdrygal"] = { target = "USdrygal", }, ["usdrypt"] = { target = "USdrypt", }, ["usdryqt"] = { target = "USdryqt", }, ["USfloz"] = { target = "USoz", }, ["usfloz"] = { target = "USoz", }, ["USGAL"] = { target = "USgal", }, ["usgal"] = { target = "USgal", }, ["usgi"] = { target = "USgi", }, ["uskenning"] = { target = "USkenning", }, ["usoz"] = { target = "USoz", }, ["uspk"] = { target = "USpk", }, ["uspt"] = { target = "USpt", }, ["usqt"] = { target = "USqt", }, ["yd3"] = { target = "cuyd", }, ["cuft/sqmi"] = { per = { "cuft", "sqmi" }, utype = "volume per unit area", default = "m3/km2", }, ["m3/ha"] = { name1 = "cubic metre per hectare", name1_us = "cubic meter per hectare", name2 = "cubic metres per hectare", name2_us = "cubic meters per hectare", symbol = "m<sup>3</sup>/ha", utype = "volume per unit area", scale = 0.0001, default = "USbu/acre", link = "Hectare", }, ["m3/km2"] = { per = { "m3", "km2" }, utype = "volume per unit area", default = "cuft/sqmi", }, ["U.S.gal/acre"] = { per = { "U.S.gal", "acre" }, utype = "volume per unit area", default = "m3/km2", }, ["USbu/acre"] = { name2 = "US bushels per acre", symbol = "US bushel per acre", usename = 1, utype = "volume per unit area", scale = 8.7077638761350888e-6, default = "m3/ha", link = "Bushel", }, ["USgal/acre"] = { per = { "USgal", "acre" }, utype = "volume per unit area", default = "m3/km2", }, ["cuyd/mi"] = { per = { "cuyd", "mi" }, utype = "volume per unit length", default = "m3/km", }, ["m3/km"] = { per = { "m3", "km" }, utype = "volume per unit length", default = "cuyd/mi", }, ["mich"] = { combination= { "ch", "mi" }, multiple = { 80 }, utype = "length", }, ["michlk"] = { combination= { "chlk", "mi" }, multiple = { 80 }, utype = "length", }, ["michainlk"] = { combination= { "chainlk", "mi" }, multiple = { 80 }, utype = "length", }, ["miyd"] = { combination= { "yd", "mi" }, multiple = { 1760 }, utype = "length", }, ["miydftin"] = { combination= { "in", "ft", "yd", "mi" }, multiple = { 12, 3, 1760 }, utype = "length", }, ["mift"] = { combination= { "ft", "mi" }, multiple = { 5280 }, utype = "length", }, ["ydftin"] = { combination= { "in", "ft", "yd" }, multiple = { 12, 3 }, utype = "length", }, ["ydft"] = { combination= { "ft", "yd" }, multiple = { 3 }, utype = "length", }, ["ftin"] = { combination= { "in", "ft" }, multiple = { 12 }, utype = "length", }, ["footin"] = { combination= { "in", "foot" }, multiple = { 12 }, utype = "length", }, ["handin"] = { combination= { "in", "hand" }, multiple = { 4 }, utype = "length", }, ["lboz"] = { combination= { "oz", "lb" }, multiple = { 16 }, utype = "mass", }, ["stlb"] = { combination= { "lb", "st" }, multiple = { 14 }, utype = "mass", }, ["stlboz"] = { combination= { "oz", "lb", "st" }, multiple = { 16, 14 }, utype = "mass", }, ["st and lb"] = { combination= { "lb", "st" }, multiple = { 14 }, utype = "mass", }, ["GN LTf"] = { combination= { "GN", "-LTf" }, utype = "force", }, ["GN LTf STf"] = { combination= { "GN", "-LTf", "-STf" }, utype = "force", }, ["GN STf"] = { combination= { "GN", "-STf" }, utype = "force", }, ["GN STf LTf"] = { combination= { "GN", "-STf", "-LTf" }, utype = "force", }, ["kN LTf"] = { combination= { "kN", "-LTf" }, utype = "force", }, ["kN LTf STf"] = { combination= { "kN", "-LTf", "-STf" }, utype = "force", }, ["kN STf"] = { combination= { "kN", "-STf" }, utype = "force", }, ["kN STf LTf"] = { combination= { "kN", "-STf", "-LTf" }, utype = "force", }, ["LTf STf"] = { combination= { "-LTf", "-STf" }, utype = "force", }, ["MN LTf"] = { combination= { "MN", "-LTf" }, utype = "force", }, ["MN LTf STf"] = { combination= { "MN", "-LTf", "-STf" }, utype = "force", }, ["MN STf"] = { combination= { "MN", "-STf" }, utype = "force", }, ["MN STf LTf"] = { combination= { "MN", "-STf", "-LTf" }, utype = "force", }, ["STf LTf"] = { combination= { "-STf", "-LTf" }, utype = "force", }, ["L/100 km mpgimp"] = { combination= { "L/100 km", "mpgimp" }, utype = "fuel efficiency", }, ["l/100 km mpgimp"] = { combination= { "l/100 km", "mpgimp" }, utype = "fuel efficiency", }, ["L/100 km mpgUS"] = { combination= { "L/100 km", "mpgus" }, utype = "fuel efficiency", }, ["L/100 km mpgus"] = { combination= { "L/100 km", "mpgus" }, utype = "fuel efficiency", }, ["l/100 km mpgus"] = { combination= { "l/100 km", "mpgus" }, utype = "fuel efficiency", }, ["mpgimp L/100 km"] = { combination= { "mpgimp", "L/100 km" }, utype = "fuel efficiency", }, ["LT ST t"] = { combination= { "lt", "-ST", "t" }, utype = "mass", }, ["LT t ST"] = { combination= { "lt", "t", "-ST" }, utype = "mass", }, ["ST LT t"] = { combination= { "-ST", "lt", "t" }, utype = "mass", }, ["ST t LT"] = { combination= { "-ST", "t", "lt" }, utype = "mass", }, ["t LT ST"] = { combination= { "t", "lt", "-ST" }, utype = "mass", }, ["ton"] = { combination= { "LT", "ST" }, utype = "mass", }, ["kPa kg/cm2"] = { combination= { "kPa", "kgf/cm2" }, utype = "pressure", }, ["kPa lb/in2"] = { combination= { "kPa", "-lb/in2" }, utype = "pressure", }, ["floz"] = { combination= { "impoz", "USoz" }, utype = "volume", }, } --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local default_exceptions = { -- Prefixed units with a default different from that of the base unit. -- Each key item is a prefixed symbol (unitcode for engineering notation). ["cm<sup>2</sup>"] = "sqin", ["dm<sup>2</sup>"] = "sqin", ["e3acre"] = "km2", ["e3m2"] = "e6sqft", ["e6acre"] = "km2", ["e6ha"] = "e6acre", ["e6km2"] = "e6sqmi", ["e6m2"] = "e6sqft", ["e6sqft"] = "v * 9.290304 < 100 ! e3 ! e6 ! m2", ["e6sqmi"] = "e6km2", ["hm<sup>2</sup>"] = "acre", ["km<sup>2</sup>"] = "sqmi", ["mm<sup>2</sup>"] = "sqin", ["aJ"] = "eV", ["e3BTU"] = "MJ", ["e6BTU"] = "GJ", ["EJ"] = "kWh", ["fJ"] = "keV", ["GJ"] = "kWh", ["MJ"] = "kWh", ["PJ"] = "kWh", ["pJ"] = "MeV", ["TJ"] = "kWh", ["YJ"] = "kWh", ["yJ"] = "μeV", ["ZJ"] = "kWh", ["zJ"] = "meV", ["e12cuft/a"] = "v * 2.8316846592 < 100 ! e9 ! e12 ! m3/a", ["e12cuft/d"] = "v * 2.8316846592 < 100 ! e9 ! e12 ! m3/d", ["e12m3/a"] = "Tcuft/a", ["e12m3/d"] = "Tcuft/d", ["e3cuft/a"] = "v * 2.8316846592 < 100 ! ! e3 ! m3/a", ["e3cuft/d"] = "v * 2.8316846592 < 100 ! ! e3 ! m3/d", ["e3cuft/s"] = "v * 2.8316846592 < 100 ! ! e3 ! m3/s", ["e3m3/a"] = "v < 28.316846592 ! k ! M ! cuft/a", ["e3m3/d"] = "v < 28.316846592 ! k ! M ! cuft/d", ["e3m3/s"] = "v < 28.316846592 ! k ! M ! cuft/s", ["e3USgal/a"] = "v * 3.785411784 < 1000 ! ! e3 ! m3/a", ["e6cuft/a"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3/a", ["e6cuft/d"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3/d", ["e6cuft/s"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3/s", ["e6m3/a"] = "v < 28.316846592 ! M ! G ! cuft/a", ["e6m3/d"] = "v < 28.316846592 ! M ! G ! cuft/d", ["e6m3/s"] = "v < 28.316846592 ! e6 ! e9 ! cuft/s", ["e6USgal/a"] = "v * 3.785411784 < 1000 ! e3 ! e6 ! m3/a", ["e9cuft/a"] = "m3/a", ["e9cuft/d"] = "v * 2.8316846592 < 100 ! e6 ! e9 ! m3/d", ["e9m3/a"] = "v < 28.316846592 ! G ! T ! cuft/a", ["e9m3/d"] = "v < 28.316846592 ! G ! T ! cuft/d", ["e9m3/s"] = "v < 28.316846592 ! e9 ! e12 ! cuft/s", ["e9USgal/a"] = "v * 3.785411784 < 1000 ! e6 ! e9 ! m3/a", ["e9USgal/s"] = "v * 3.785411784 < 1000 ! e6 ! e9 ! m3/s", ["nN"] = "gr-f", ["μN"] = "gr-f", ["mN"] = "oz-f", ["am"] = "in", ["cm"] = "in", ["dam"] = "ft", ["dm"] = "in", ["e12km"] = "e12mi", ["e12mi"] = "e12km", ["e3AU"] = "ly", ["e3km"] = "e3mi", ["e3mi"] = "e3km", ["e6km"] = "e6mi", ["e6mi"] = "e6km", ["e9km"] = "AU", ["e9mi"] = "e9km", ["Em"] = "mi", ["fm"] = "in", ["Gm"] = "mi", ["hm"] = "ft", ["km"] = "mi", ["mm"] = "in", ["Mm"] = "mi", ["nm"] = "in", ["Pm"] = "mi", ["pm"] = "in", ["Tm"] = "mi", ["Ym"] = "mi", ["ym"] = "in", ["Zm"] = "mi", ["zm"] = "in", ["μm"] = "in", ["e12lb"] = "v * 4.5359237 < 10 ! Mt ! Gt", ["e3lb"] = "v * 4.5359237 < 10 ! kg ! t", ["e3ozt"] = "v * 0.311034768 < 10 ! kg ! t", ["e3t"] = "LT ST", ["e6carat"] = "t", ["e6lb"] = "v * 4.5359237 < 10 ! t ! kilotonne", ["e6ozt"] = "lb kg", ["e6ST"] = "Mt", ["e6t"] = "LT ST", ["e9lb"] = "v * 4.5359237 < 10 ! kilotonne ! Mt", ["e9t"] = "LT ST", ["Gg"] = "lb", ["kg"] = "lb", ["mg"] = "gr", ["Mg"] = "LT ST", ["ng"] = "gr", ["μg"] = "gr", ["mBq"] = "fCi", ["kBq"] = "nCi", ["MBq"] = "μCi", ["GBq"] = "mCi", ["TBq"] = "Ci", ["PBq"] = "kCi", ["EBq"] = "kCi", ["fCi"] = "mBq", ["pCi"] = "Bq", ["nCi"] = "Bq", ["μCi"] = "kBq", ["mCi"] = "MBq", ["kCi"] = "TBq", ["MCi"] = "PBq", ["ns"] = "μs", ["μs"] = "ms", ["ms"] = "s", ["ks"] = "h", ["Ms"] = "week", ["Gs"] = "decade", ["Ts"] = "millennium", ["Ps"] = "million year", ["Es"] = "thousand million year", ["MK"] = "keVT", ["cL"] = "impoz usoz", ["cl"] = "impoz usoz", ["cm<sup>3</sup>"] = "cuin", ["dL"] = "impoz usoz", ["dl"] = "impoz usoz", ["mm<sup>3</sup>"] = "cuin", ["dm<sup>3</sup>"] = "cuin", ["e12cuft"] = "v * 2.8316846592 < 100 ! e9 ! e12 ! m3", ["e12impgal"] = "v * 4.54609 < 1000 ! T ! P ! l", ["e12m3"] = "v < 28.316846592 ! T ! P ! cuft", ["e12U.S.gal"] = "v * 3.785411784 < 1000 ! T ! P ! l", ["e12USgal"] = "v * 3.785411784 < 1000 ! T ! P ! l", ["e15cuft"] = "v * 2.8316846592 < 100 ! e12 ! e15 ! m3", ["e15m3"] = "Pcuft", ["e3bdft"] = "v * 0.23597372167 < 100 ! e3 ! e6 ! m3", ["e3cuft"] = "v * 2.8316846592 < 100 ! ! e3 ! m3", ["e3impgal"] = "v * 4.54609 < 1000 ! k ! M ! l", ["e3m3"] = "v < 28.316846592 ! k ! M ! cuft", ["e3U.S.gal"] = "v * 3.785411784 < 1000 ! k ! M ! l", ["e3USgal"] = "v * 3.785411784 < 1000 ! k ! M ! l", ["e6bdft"] = "v * 0.23597372167 < 100 ! e3 ! e6 ! m3", ["e6cuft"] = "v * 2.8316846592 < 100 ! e3 ! e6 ! m3", ["e6cuyd"] = "v * 7.64554857984 < 10 ! e3 ! e6 ! m3", ["e6impgal"] = "v * 4.54609 < 1000 ! M ! G ! l", ["e6L"] = "USgal", ["e6m3"] = "v < 28.316846592 ! M ! G ! cuft", ["e6U.S.gal"] = "v * 3.785411784 < 1000 ! M ! G ! l", ["e6USgal"] = "v * 3.785411784 < 1000 ! M ! G ! l", ["e9bdft"] = "v * 0.23597372167 < 100 ! e6 ! e9 ! m3", ["e9cuft"] = "v * 2.8316846592 < 100 ! e6 ! e9 ! m3", ["e9impgal"] = "v * 4.54609 < 1000 ! G ! T ! l", ["e9m3"] = "v < 28.316846592 ! G ! T ! cuft", ["e9U.S.gal"] = "v * 3.785411784 < 1000 ! G ! T ! l", ["e9USgal"] = "v * 3.785411784 < 1000 ! G ! T ! l", ["GL"] = "cuft", ["Gl"] = "cuft", ["kL"] = "cuft", ["kl"] = "cuft", ["km<sup>3</sup>"] = "cumi", ["mL"] = "impoz usoz", ["ml"] = "impoz usoz", ["Ml"] = "v < 28.316846592 ! e3 ! e6 ! cuft", ["ML"] = "v < 28.316846592 ! e3 ! e6 ! cuft", ["TL"] = "cumi", ["Tl"] = "cumi", ["μL"] = "cuin", ["μl"] = "cuin", } --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local link_exceptions = { -- Prefixed units with a linked article different from that of the base unit. -- Each key item is a prefixed symbol (not unitcode). ["mm<sup>2</sup>"] = "Square millimetre", ["cm<sup>2</sup>"] = "Square centimetre", ["dm<sup>2</sup>"] = "Square decimetre", ["km<sup>2</sup>"] = "Square kilometre", ["kJ"] = "Kilojoule", ["MJ"] = "Megajoule", ["GJ"] = "Gigajoule", ["TJ"] = "Terajoule", ["fm"] = "Femtometre", ["pm"] = "Picometre", ["nm"] = "Nanometre", ["μm"] = "Micrometre", ["mm"] = "Millimetre", ["cm"] = "Centimetre", ["dm"] = "Decimetre", ["dam"] = "Decametre", ["hm"] = "Hectometre", ["km"] = "Kilometre", ["Mm"] = "Megametre", ["Gm"] = "Gigametre", ["Tm"] = "Terametre", ["Pm"] = "Petametre", ["Em"] = "Exametre", ["Zm"] = "Zettametre", ["Ym"] = "Yottametre", ["μg"] = "Microgram", ["mg"] = "Milligram", ["kg"] = "Kilogram", ["Mg"] = "Tonne", ["yW"] = "Yoctowatt", ["zW"] = "Zeptowatt", ["aW"] = "Attowatt", ["fW"] = "Femtowatt", ["pW"] = "Picowatt", ["nW"] = "Nanowatt", ["μW"] = "Microwatt", ["mW"] = "Milliwatt", ["kW"] = "Kilowatt", ["MW"] = "Megawatt", ["GW"] = "Gigawatt", ["TW"] = "Terawatt", ["PW"] = "Petawatt", ["EW"] = "Exawatt", ["ZW"] = "Zettawatt", ["YW"] = "Yottawatt", ["as"] = "Attosecond", ["fs"] = "Femtosecond", ["ps"] = "Picosecond", ["ns"] = "Nanosecond", ["μs"] = "Microsecond", ["ms"] = "Millisecond", ["ks"] = "Kilosecond", ["Ms"] = "Megasecond", ["Gs"] = "Gigasecond", ["Ts"] = "Terasecond", ["Ps"] = "Petasecond", ["Es"] = "Exasecond", ["Zs"] = "Zettasecond", ["Ys"] = "Yottasecond", ["mm<sup>3</sup>"] = "Cubic millimetre", ["cm<sup>3</sup>"] = "Cubic centimetre", ["dm<sup>3</sup>"] = "Cubic decimetre", ["dam<sup>3</sup>"] = "Cubic decametre", ["km<sup>3</sup>"] = "Cubic kilometre", ["μL"] = "Microlitre", ["μl"] = "Microlitre", ["mL"] = "Millilitre", ["ml"] = "Millilitre", ["cL"] = "Centilitre", ["cl"] = "Centilitre", ["dL"] = "Decilitre", ["dl"] = "Decilitre", ["daL"] = "Decalitre", ["dal"] = "Decalitre", ["hL"] = "Hectolitre", ["hl"] = "Hectolitre", ["kL"] = "Kilolitre", ["kl"] = "Kilolitre", ["ML"] = "Megalitre", ["Ml"] = "Megalitre", ["GL"] = "Gigalitre", ["Gl"] = "Gigalitre", ["TL"] = "Teralitre", ["Tl"] = "Teralitre", ["PL"] = "Petalitre", ["Pl"] = "Petalitre", } --------------------------------------------------------------------------- -- Do not change the data in this table because it is created by running -- -- a script that reads the wikitext from a wiki page (see note above). -- --------------------------------------------------------------------------- local per_unit_fixups = { -- Automatically created per units of form "x/y" may have their unit type -- changed, for example, "length/time" is changed to "speed". -- Other adjustments can also be specified. ["/area"] = "per unit area", ["/volume"] = "per unit volume", ["area/area"] = "area per unit area", ["energy/length"] = "energy per unit length", ["energy/mass"] = "energy per unit mass", ["energy/time"] = { utype = "power", link = "Power (physics)" }, ["energy/volume"] = "energy per unit volume", ["force/area"] = { utype = "pressure", link = "Pressure" }, ["length/length"] = { utype = "gradient", link = "Grade (slope)" }, ["length/time"] = { utype = "speed", link = "Speed" }, ["length/time/time"] = { utype = "acceleration", link = "Acceleration" }, ["mass/area"] = { utype = "pressure", multiplier = 9.80665 }, ["mass/length"] = "linear density", ["mass/mass"] = "concentration", ["mass/power"] = "mass per unit power", ["mass/time"] = "mass per unit time", ["mass/volume"] = { utype = "density", link = "Density" }, ["power/mass"] = "power per unit mass", ["power/volume"] = { link = "Power density" }, ["pressure/length"] = "fracture gradient", ["speed/time"] = { utype = "acceleration", link = "Acceleration" }, ["volume/area"] = "volume per unit area", ["volume/length"] = "volume per unit length", ["volume/time"] = "flow", } return { all_units = all_units, default_exceptions = default_exceptions, link_exceptions = link_exceptions, per_unit_fixups = per_unit_fixups, } 8d4f7e3d03f55a7683bae4e0b800b77ac91d44a2 Module:Convert/text 828 498 997 996 2023-07-10T16:51:32Z BigTa1k 2 1 revision imported Scribunto text/plain -- Text used by Module:Convert for enwiki. -- This is a separate module to simplify translation for use on another wiki. -- See [[:en:Template:Convert/Transwiki guide]] if copying to another wiki. -- Some units accept an SI prefix before the unit code, such as "kg" for kilogram. local SIprefixes = { -- The prefix field is what the prefix should be, if different from the prefix used. ['Q'] = { exponent = 30, name = 'quetta', }, ['R'] = { exponent = 27, name = 'ronna', }, ['Y'] = { exponent = 24, name = 'yotta', }, ['Z'] = { exponent = 21, name = 'zetta', }, ['E'] = { exponent = 18, name = 'exa' , }, ['P'] = { exponent = 15, name = 'peta' , }, ['T'] = { exponent = 12, name = 'tera' , }, ['G'] = { exponent = 9, name = 'giga' , }, ['M'] = { exponent = 6, name = 'mega' , }, ['k'] = { exponent = 3, name = 'kilo' , }, ['h'] = { exponent = 2, name = 'hecto', }, ['da']= { exponent = 1, name = 'deca' , name_us = 'deka' }, ['d'] = { exponent = -1, name = 'deci' , }, ['c'] = { exponent = -2, name = 'centi', }, ['m'] = { exponent = -3, name = 'milli', }, ['μ'] = { exponent = -6, name = 'micro', }, -- key = 'GREEK SMALL LETTER MU' (U+03BC) utf-8 CE BC ['µ'] = { exponent = -6, name = 'micro', prefix = 'μ' }, -- key = 'MICRO SIGN' (U+00B5) utf-8 C2 B5 ['u'] = { exponent = -6, name = 'micro', prefix = 'μ' }, -- not an SI prefix, but allow for people typing this ['n'] = { exponent = -9, name = 'nano' , }, ['p'] = { exponent =-12, name = 'pico' , }, ['f'] = { exponent =-15, name = 'femto', }, ['a'] = { exponent =-18, name = 'atto' , }, ['z'] = { exponent =-21, name = 'zepto', }, ['y'] = { exponent =-24, name = 'yocto', }, ['r'] = { exponent =-27, name = 'ronto', }, ['q'] = { exponent =-30, name = 'quecto', }, } -- Some units can be qualified with one of the following prefixes, when linked. local customary_units = { { "US", link = "United States customary units" }, { "U.S.", link = "United States customary units" }, { "imperial", link = "Imperial units" }, { "imp", link = "Imperial units" }, } -- Names when using engineering notation (a prefix of "eN" where N is a number; example "e6km"). -- key = { "name", link = "article title", exponent = numeric_key_value } -- If lk=on and link is defined, the name of the number will appear as a link. local eng_scales = { ["3"] = { "thousand", exponent = 3 }, ["6"] = { "million", exponent = 6 }, ["9"] = { "billion", link = "1000000000 (number)", exponent = 9 }, ["12"] = { "trillion", link = "1000000000000 (number)", exponent = 12 }, ["15"] = { "quadrillion", link = "1000000000000000 (number)", exponent = 15 }, } local all_categories = { unit = "[[Category:Convert errors]]", option = "[[Category:Convert errors]]", warning = '[[Category:Convert invalid options]]', tracking = '[[Category:Convert tracking]]', } -- For some error messages, the following puts the wanted style around -- each unit code marked like '...%{ft%}...'. local unitcode_regex = '%%([{}])' local unitcode_replace = { ['{'] = '"', ['}'] = '"' } -- no longer need the more elaborate substitute used before 2013-09-28 -- All messages that may be displayed if a problem occurs. local all_messages = { -- Message format string: $1=title, $2=text, $3=category, $4=anchor. -- Each displayed message starts with "Convert:" so can easily locate by searching article. cvt_format = '<sup class="noprint Inline-Template" style="white-space:nowrap;">[<i>[[Help:Convert messages#$4|<span title="Convert: $1">convert: $2</span>]]</i>]</sup>$3<span class="error"></span>', cvt_format2 = '<sup class="noprint Inline-Template" style="white-space:nowrap;">[[Help:Convert messages#$4|<span title="Convert: $1">$2</span>]]</sup>$3<span class="error"></span>', cvt_format_preview = '<strong class="error">Error in convert: $1 [[Help:Convert messages#$4|(help)]]</strong>$3', -- Each of following messages is a table: -- { [1] = 'title', -- mouseover title text -- [2] = 'text', -- link text displayed in article -- [3] = 'category key', -- key to lookup category in all_categories -- [4] = 'anchor', -- anchor for link to relevant section on help page -- regex = gsub_regex, -- replace = gsub_table, -- } Mouseover title text Link text CatKey Anchor cvt_bad_input = { 'input "$1" must be a number and unit' , 'invalid input' , 'option', 'invalid_input' }, cvt_bad_num = { 'Value "$1" must be a number' , 'invalid number' , 'option', 'invalid_number' }, cvt_big_prec = { 'Precision "$1" is too large' , 'precision too large' , 'option', 'precision_too_large' }, cvt_invalid_num = { 'Number has overflowed' , 'number overflow' , 'option', 'number_overflow' }, cvt_no_num = { 'Needs the number to be converted' , 'needs a number' , 'option', 'needs_number' }, cvt_no_num2 = { 'Needs another number for a range' , 'needs another number', 'option', 'needs_another_number' }, cvt_bad_altitude = { '"$1" needs an integer' , 'invalid altitude' , 'option', 'invalid_altitude' }, cvt_bad_frac = { '"$1" needs an integer above 1' , 'invalid fraction' , 'option', 'invalid_fraction' }, cvt_bad_prec = { 'Precision "$1" must be an integer' , 'invalid precision' , 'option', 'invalid_precision' }, cvt_bad_sigfig = { '"$1" needs a positive integer' , 'invalid sigfig' , 'option', 'invalid_sigfig' }, cvt_empty_option = { 'Ignored empty option "$1"' , 'empty option' , 'option', 'empty_option' }, cvt_deprecated = { 'Option "$1" is deprecated' , '*' , 'option', 'deprecated_option', format = 'cvt_format2', nowarn = true }, cvt_no_spell = { 'Spelling is not available' , 'bug, ask for help' , 'option', 'ask_for_help' }, cvt_unknown_option = { 'Ignored invalid option "$1"' , 'invalid option' , 'option', 'invalid_option' }, cvt_wd_fail = { 'Unable to access Wikidata' , 'wikidata problem' , 'option', 'wikidata_problem' }, cvt_bad_default = { 'Unit "$1" has an invalid default' , 'bug, ask for help' , 'unit' , 'ask_for_help' }, cvt_bad_unit = { 'Unit "$1" is invalid here' , 'unit invalid here' , 'unit' , 'unit_invalid_here' }, cvt_no_default = { 'Unit "$1" has no default output unit' , 'bug, ask for help' , 'unit' , 'ask_for_help' }, cvt_no_unit = { 'Needs name of unit' , 'needs unit name' , 'unit' , 'needs_unit_name' }, cvt_unknown = { 'Unit name "$1" is not known' , 'unknown unit' , 'unit' , 'unknown_unit' }, cvt_should_be = { '$1' , 'ambiguous unit' , 'unit' , 'ambiguous_unit', regex = unitcode_regex, replace = unitcode_replace }, cvt_mismatch = { 'Cannot convert "$1" to "$2"' , 'unit mismatch' , 'unit' , 'unit_mismatch' }, cvt_bug_convert = { 'Bug: Cannot convert between specified units', 'bug, ask for help' , 'unit' , 'ask_for_help' }, cvt_lookup = { 'Unit "$1" is incorrectly defined' , 'bug, ask for help' , 'unit' , 'ask_for_help' }, } -- Text to join input value/unit with output value/unit. local disp_joins = { -- [1]=before output, [2]=after output, [3]=between outputs in a combination; default "; " -- [wantname] gives default abbr=off ["or"] = { " or " , "" , " or ", wantname = true }, ["sqbr-sp"] = { " [" , "]" }, ["sqbr-nbsp"] = { "&nbsp;[" , "]" }, ["comma"] = { ", " , "" , ", " }, ["semicolon"] = { "; " , "" }, ["slash-sp"] = { " / " , "" , wantname = true }, ["slash-nbsp"] = { "&nbsp;/ ", "" , wantname = true }, ["slash-nosp"] = { "/" , "" , wantname = true }, ["b"] = { " (" , ")" }, ["(or)"] = { " (" , ")", " or " }, ["br"] = { "<br />" , "" , wantname = true }, ["br()"] = { "<br />(" , ")", wantname = true }, } -- Text to separate values in a range. local range_types = { -- Specifying a table requires either: -- * "off" and "on" values (for "abbr=off" and "abbr=on"), or -- * "input" and "output" values (for LHS and RHS); -- other fields are optional. -- When "adj=on|abbr=off" applies, spaces in range text are replaced with hyphens. -- With "exception = true", that also occurs with "adj=on|abbr=on". -- If "adj" is defined here, that text (unchanged) is used with "adj=on". ["+"] = " + ", [","] = ",&nbsp;", [", and"] = ", and ", [", or"] = ", or ", ["by"] = " by ", ["-"] = "–", ["to about"] = " to about ", ["and"] = { off = " and ", on = " and ", exception = true }, ["and(-)"] = { input = " and ", output = "–" }, ["or"] = { off = " or " , on = " or " , exception = true }, ["to"] = { off = " to " , on = " to " , exception = true }, ["to(-)"] = { input = "&nbsp;to ", output = "–" }, ["+/-"] = { off = "&nbsp;±&nbsp;", on = "&nbsp;±&nbsp;", adj = "&nbsp;±&nbsp;", is_range_change = true }, ["by(x)"] = { input = " by ", output = " ×&nbsp;", out_range_x = true }, ["x"] = { off = " by ", on = " ×&nbsp;", abbr_range_x = true }, ["xx"] = "&nbsp;×&nbsp;", ["*"] = "×", ["/"] = "&thinsp;/&thinsp;", -- for a table of high/low temperatures with {{convert|83|/|63|F|disp=br()|abbr=values}} } local range_aliases = { -- ["alternative name for a range"] = "standard range name" ["–"] = "-", ["&ndash;"] = "-", ["×"] = "x", ["&times;"] = "x", ["±"] = "+/-", ["&plusmn;"] = "+/-", } -- Convert accepts range text delimited with whitespace, for example, {{convert|1 to 2|ft}}. -- In addition, the following "words" are accepted without spaces, for example, {{convert|1-2|ft}}. -- Words must be in correct order for searching, for example, 'x' after 'xx'. local range_words = { '-', '–', 'xx', 'x', '*' } local ranges = { types = range_types, aliases = range_aliases, words = range_words, } -- Valid option names. local en_option_name = { -- ["local text for option name"] = "en name used in this module" ["$"] = "$", ["abbr"] = "abbr", ["adj"] = "adj", ["altitude_ft"] = "altitude_ft", ["altitude_m"] = "altitude_m", ["comma"] = "comma", ["debug"] = "debug", ["disp"] = "disp", ["frac"] = "frac", ["input"] = "input", ["lang"] = "lang", ["lk"] = "lk", ["order"] = "order", ["qid"] = "qid", ["qual"] = "qual", ["qualifier"] = "qual", ["round"] = "round", ["sigfig"] = "sigfig", ["sing"] = "adj", -- "sing" is an old alias for "adj" ["sortable"] = "sortable", ["sp"] = "sp", ["spell"] = "spell", ["stylein"] = "stylein", ["styleout"] = "styleout", ["tracking"] = "tracking", } -- Valid option values. -- Convention: parms.opt_xxx refers to an option that is set here -- (not intended to be set by the template which invokes this module). -- Example: At enwiki, "abbr" includes: -- ["values"] = "opt_values" -- As a result, if the template uses abbr=values, Module:Convert sets: -- parms["opt_values"] = true -- parms["abbr"] = nil -- Therefore parms.abbr will be nil, or will have one of the listed values -- that do not start with "opt_". -- An option value of form "xxx?" is the same as "xxx" but shows the input as deprecated. local en_option_value = { ["$"] = 'TEXT', -- TEXT should be a currency symbol that will be used instead of "$" ["abbr"] = { -- ["local text for option value"] = "en value used in this module" ["def"] = "", -- ignored (some wrapper templates call convert with "abbr=def" to mean "default abbreviation") ["h"] = "on", -- abbr=on + use "h" for hand unit (default) ["hh"] = "opt_hand_hh", -- abbr=on + use "hh" for hand unit ["in"] = "in", -- use symbol for LHS unit ["none"] = "off", -- old name for "off" ["off"] = "off", -- use name for all units ["on"] = "on", -- use symbol for all units ["out"] = "out", -- use symbol for RHS unit (default) ["unit"] = "unit", -- abbr=on but abbreviate units only: e6km → million km (not ×10⁶ km) ["values"] = "opt_values", -- show only input and output numbers, not units ["~"] = "opt_also_symbol", -- show input unit symbol as well as name }, ["adj"] = { ["mid"] = "opt_adjectival, opt_adj_mid", -- adj=on with user-specified text after input unit (between input and output) ["off"] = "", -- ignored (off is the default) ["on"] = "opt_adjectival", -- unit name is singular and hyphenated ["pre"] = "opt_one_preunit", -- user-specified text before input unit ["ri0"] = "opt_ri=0", -- round input with precision = 0 ["ri1"] = "opt_ri=1", -- round input with precision = 1 ["ri2"] = "opt_ri=2", -- round input with precision = 2 ["ri3"] = "opt_ri=3", -- round input with precision = 3 }, ["altitude_ft"] = 'INTEGER', ["altitude_m"] = 'INTEGER', ["comma"] = { ["5"] = "opt_comma5", -- only use numsep grouping if 5 or more digits ["gaps"] = "opt_gaps", -- use gaps, not numsep, to separate groups of digits ["gaps3"] = "opt_gaps, opt_gaps3", -- group only in threes rather than default of no gap before a single digit after decimal mark ["off"] = "opt_nocomma", -- no numsep in input or output numbers }, ["debug"] = { ["yes"] = "opt_sortable_debug", -- make the normally hidden sort key visible }, ["disp"] = { ["5"] = "opt_round=5?", -- round output value to nearest 5 ["b"] = "b", -- join: '(...)' ["(or)"] = "(or)", -- join: '(...)' with 'or' between outputs in a combination ["br"] = "br", -- join: '<br />' ["br()"] = "br()", -- join: '<br />(...)' ["comma"] = "comma", -- join: ',' ["flip"] = "opt_flip", -- reverse order of input/output ["number"] = "opt_output_number_only", -- display output value (not input, and not output symbol/name) ["or"] = "or", -- join: 'or' ["out"] = "opt_output_only", ["output number only"] = "opt_output_number_only", ["output only"] = "opt_output_only", ["preunit"] = "opt_two_preunits", -- user-specified text before input and output units ["semicolon"] = "semicolon", -- join: ';' ["sqbr"] = "sqbr", -- join: '[...]' ["table"] = "opt_table", -- output is suitable for a table cell with align="right" ["tablecen"] = "opt_tablecen", -- output is suitable for a table cell with align="center" ["unit"] = "opt_input_unit_only", -- display input symbol/name (not output, and not input value) ["unit or text"] = "opt_input_unit_only, opt_ignore_error", -- display input symbol/name, or given unit code if not known ["unit2"] = "opt_output_unit_only", ["x"] = "x", -- join: <first>...<second> (user-specified text) }, ["frac"] = 'INTEGER', ["input"] = 'TEXT', -- TEXT should be value><space><unitcode> or <wikidata-property-id> ["lang"] = { -- language for output digits (both en and local digits are always accepted for input) ["en"] = "opt_lang_en", -- use en digits for numbers, regardless of local language ["local"] = "opt_lang_local", -- use local digits for numbers (default, although config can change default to en) }, ["lk"] = { ["in"] = "in", -- link LHS unit name or symbol ["off"] = "off", -- do not link: same as default except for hand unit ["on"] = "on", -- link all unit names or symbols (but not twice for the same unit) ["out"] = "out", -- link RHS unit name or symbol }, ["order"] = { ["flip"] = "opt_flip", -- reverse order of input/output ["out"] = "opt_order_out", -- do not show input; instead, use order in output combination, with the first output shown as the input }, ["qid"] = 'TEXT', -- TEXT should be a Wikidata Q item identifier ["qual"] = 'TEXT', -- TEXT should be a Wikidata Q item identifier ["round"] = { ["0.5"] = "opt_round=0.5", -- round output value to nearest 0.5 ["5"] = "opt_round=5", -- round output value to nearest 5 ["10"] = "opt_round=10", -- round output value to nearest 10 (same as but clearer than "|-1") ["25"] = "opt_round=25", -- round output value to nearest 25 ["50"] = "opt_round=50", -- round output value to nearest 50 ["each"] = "opt_round_each", -- using default precision in a range, round each output separately (default uses highest precision of each item in range) }, ["sigfig"] = 'INTEGER', ["sortable"] = { ["off"] = "", -- ignored (off is the default) ["on"] = "opt_sortable_on", -- output sort key for use in a sortable table, based on value from converting to a standard base unit ["debug"] = "opt_sortable_on, opt_sortable_debug", -- |sortable=debug is the same as |sortable=on|debug=yes }, ["sp"] = { ["us"] = "opt_sp_us", -- use U.S. spelling (like "meter" instead of default "metre") }, ["spell"] = { -- only English spelling is supported; not scientific notation; only some fractions ["in"] = "opt_spell_in", -- spell input value in words ["In"] = "opt_spell_in, opt_spell_upper", -- spell input value in words with first letter uppercase ["on"] = "opt_spell_in, opt_spell_out", -- spell input and output values in words ["On"] = "opt_spell_in, opt_spell_out, opt_spell_upper", -- same, with first letter of first word in result uppercase ["us"] = "opt_sp_us", -- use U.S. spelling; same as sp=us so spell=us also works }, ["stylein"] = 'TEXT', ["styleout"] = 'TEXT', ["tracking"] = 'TEXT', } local titles = { ["frac"] = "Fraction/styles.css", ["sfrac"] = "Sfrac/styles.css", } return { SIprefixes = SIprefixes, all_categories = all_categories, all_messages = all_messages, currency = { ['$'] = true, ['£'] = true, ['€'] = true, ['₱'] = true, ['₽'] = true, ['¥'] = true }, customary_units = customary_units, disp_joins = disp_joins, en_option_name = en_option_name, en_option_value = en_option_value, eng_scales = eng_scales, ranges = ranges, titles = titles, } ff98cf24da87736f3469f82401084ca608335d55 Module:Hlist 828 499 999 998 2023-07-10T16:51:33Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:list|horizontal}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 9e3824c2e3c0e0dbef2f37556ac0b994987fecf9 Module:Separated entries 828 500 1001 1000 2023-07-10T16:51:34Z BigTa1k 2 1 revision imported Scribunto text/plain -- This module takes positional parameters as input and concatenates them with -- an optional separator. The final separator (the "conjunction") can be -- specified independently, enabling natural-language lists like -- "foo, bar, baz and qux". The starting parameter can also be specified. local compressSparseArray = require('Module:TableTools').compressSparseArray local p = {} function p._main(args) local separator = args.separator -- Decode (convert to Unicode) HTML escape sequences, such as "&#32;" for space. and mw.text.decode(args.separator) or '' local conjunction = args.conjunction and mw.text.decode(args.conjunction) or separator -- Discard values before the starting parameter. local start = tonumber(args.start) if start then for i = 1, start - 1 do args[i] = nil end end -- Discard named parameters. local values = compressSparseArray(args) return mw.text.listToText(values, separator, conjunction) end local function makeInvokeFunction(separator, conjunction, first) return function (frame) local args = require('Module:Arguments').getArgs(frame) args.separator = separator or args.separator args.conjunction = conjunction or args.conjunction args.first = first or args.first return p._main(args) end end p.main = makeInvokeFunction() p.br = makeInvokeFunction('<br />') p.comma = makeInvokeFunction(mw.message.new('comma-separator'):plain()) return p e80231ff3de01afd7f62a94e0a34dc1e67504085 Module:Xt 828 501 1003 1002 2023-07-10T16:51:35Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{#ifeq:{{NAMESPACE}}|{{ns:0}}|{{FormattingError|[[:{{#invoke:TEMPLATENAME|main}}]] is only for examples of style and formatting. Do not use it in actual articles.}}|<span class="example" style="font-family: Georgia, 'DejaVu Serif', serif; color: #006400;" {{#if:{{{title|}}}|title="{{{title}}}"}}>{{{1|Example text}}}</span>}}<noinclude> {{Documentation}} </noinclude> e8c5895953384f68b9648a698f7f33d79748e408 Module:GetParameters 828 502 1007 1006 2023-07-10T16:51:36Z BigTa1k 2 1 revision imported Scribunto text/plain local p = {} --[[ Helper function that populates the argument list given that user may need to use a mix of named and unnamed parameters. This is relevant because named parameters are not identical to unnamed parameters due to string trimming, and when dealing with strings we sometimes want to either preserve or remove that whitespace depending on the application. ]] function p.getParameters( frame_args, arg_list ) local new_args = {}; local index = 1; local value; for i,arg in ipairs( arg_list ) do value = frame_args[arg] if value == nil then value = frame_args[index]; index = index + 1; end new_args[arg] = value; end return new_args; end --[[ Helper Function to interpret boolean strings ]] function p.getBoolean( boolean_str ) local boolean_value; if type( boolean_str ) == 'string' then boolean_str = boolean_str:lower(); if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0' or boolean_str == '' then boolean_value = false; else boolean_value = true; end elseif type( boolean_str ) == 'boolean' then boolean_value = boolean_str; else error( 'No boolean value found' ); end return boolean_value end function p.defined(frame) local arg = mw.text.trim(frame.args[1]) --if arg == tostring(tonumber(arg)) then -- undesired result for '-0' -- arg = tonumber(arg) --end --if mw.ustring.find(arg, '^%s*-?[1-9][0-9]*%s*$') ~= nil or arg == '0' then -- arg = tonumber(arg) --end if mw.ustring.find(arg, '^-?[1-9][0-9]*$') ~= nil then arg = tonumber(arg) elseif arg == '0' then arg = 0 end return frame:getParent().args[arg] ~= nil end return p 00e952f0ee8f6ea68e990d589dfb15e7d4036623 Module:For loop 828 503 1009 1008 2023-07-10T16:51:41Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:For loop|main}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 12b7b7010fe32f888e21bcdfa6a904fc8a925437 1011 1009 2023-07-10T16:51:41Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{<includeonly>safesubst:</includeonly>#invoke:For loop|main}}<noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> 12b7b7010fe32f888e21bcdfa6a904fc8a925437 Module:For 828 504 1013 1012 2023-07-10T16:51:41Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly>{{#invoke:For|For}}</includeonly><noinclude> {{Documentation}} </noinclude> 3f70c0fa7cd736071e7c6e7dcd90ff3704df26bb 1015 1013 2023-07-10T16:51:42Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly>{{#invoke:For|For}}</includeonly><noinclude> {{Documentation}} </noinclude> 3f70c0fa7cd736071e7c6e7dcd90ff3704df26bb Module:!- 828 505 1017 1016 2023-07-10T16:51:42Z BigTa1k 2 1 revision imported wikitext text/x-wiki |-<noinclude> {{documentation}} </noinclude> 19a1b27b5273caa6fd83a1208fb704afa9ee7c04 Module:!! 828 506 1019 1018 2023-07-10T16:51:43Z BigTa1k 2 1 revision imported wikitext text/x-wiki ||<noinclude> {{documentation}} </noinclude> cfbaaca3cb3edae590f75b87fb775d1be21774a4 Module:ConvertNumeric 828 507 1021 1020 2023-07-10T16:51:43Z BigTa1k 2 1 revision imported Scribunto text/plain -- Module for converting between different representations of numbers. See talk page for user documentation. -- For unit tests see: [[Module:ConvertNumeric/testcases]] -- When editing, preview with: [[Module_talk:ConvertNumeric/testcases]] -- First, edit [[Module:ConvertNumeric/sandbox]], then preview with [[Module_talk:ConvertNumeric/sandbox/testcases]] require('strict') local ones_position = { [0] = 'zero', [1] = 'one', [2] = 'two', [3] = 'three', [4] = 'four', [5] = 'five', [6] = 'six', [7] = 'seven', [8] = 'eight', [9] = 'nine', [10] = 'ten', [11] = 'eleven', [12] = 'twelve', [13] = 'thirteen', [14] = 'fourteen', [15] = 'fifteen', [16] = 'sixteen', [17] = 'seventeen', [18] = 'eighteen', [19] = 'nineteen' } local ones_position_ord = { [0] = 'zeroth', [1] = 'first', [2] = 'second', [3] = 'third', [4] = 'fourth', [5] = 'fifth', [6] = 'sixth', [7] = 'seventh', [8] = 'eighth', [9] = 'ninth', [10] = 'tenth', [11] = 'eleventh', [12] = 'twelfth', [13] = 'thirteenth', [14] = 'fourteenth', [15] = 'fifteenth', [16] = 'sixteenth', [17] = 'seventeenth', [18] = 'eighteenth', [19] = 'nineteenth' } local ones_position_plural = { [0] = 'zeros', [1] = 'ones', [2] = 'twos', [3] = 'threes', [4] = 'fours', [5] = 'fives', [6] = 'sixes', [7] = 'sevens', [8] = 'eights', [9] = 'nines', [10] = 'tens', [11] = 'elevens', [12] = 'twelves', [13] = 'thirteens', [14] = 'fourteens', [15] = 'fifteens', [16] = 'sixteens', [17] = 'seventeens', [18] = 'eighteens', [19] = 'nineteens' } local tens_position = { [2] = 'twenty', [3] = 'thirty', [4] = 'forty', [5] = 'fifty', [6] = 'sixty', [7] = 'seventy', [8] = 'eighty', [9] = 'ninety' } local tens_position_ord = { [2] = 'twentieth', [3] = 'thirtieth', [4] = 'fortieth', [5] = 'fiftieth', [6] = 'sixtieth', [7] = 'seventieth', [8] = 'eightieth', [9] = 'ninetieth' } local tens_position_plural = { [2] = 'twenties', [3] = 'thirties', [4] = 'forties', [5] = 'fifties', [6] = 'sixties', [7] = 'seventies', [8] = 'eighties', [9] = 'nineties' } local groups = { [1] = 'thousand', [2] = 'million', [3] = 'billion', [4] = 'trillion', [5] = 'quadrillion', [6] = 'quintillion', [7] = 'sextillion', [8] = 'septillion', [9] = 'octillion', [10] = 'nonillion', [11] = 'decillion', [12] = 'undecillion', [13] = 'duodecillion', [14] = 'tredecillion', [15] = 'quattuordecillion', [16] = 'quindecillion', [17] = 'sexdecillion', [18] = 'septendecillion', [19] = 'octodecillion', [20] = 'novemdecillion', [21] = 'vigintillion', [22] = 'unvigintillion', [23] = 'duovigintillion', [24] = 'tresvigintillion', [25] = 'quattuorvigintillion', [26] = 'quinquavigintillion', [27] = 'sesvigintillion', [28] = 'septemvigintillion', [29] = 'octovigintillion', [30] = 'novemvigintillion', [31] = 'trigintillion', [32] = 'untrigintillion', [33] = 'duotrigintillion', [34] = 'trestrigintillion', [35] = 'quattuortrigintillion', [36] = 'quinquatrigintillion', [37] = 'sestrigintillion', [38] = 'septentrigintillion', [39] = 'octotrigintillion', [40] = 'noventrigintillion', [41] = 'quadragintillion', [51] = 'quinquagintillion', [61] = 'sexagintillion', [71] = 'septuagintillion', [81] = 'octogintillion', [91] = 'nonagintillion', [101] = 'centillion', [102] = 'uncentillion', [103] = 'duocentillion', [104] = 'trescentillion', [111] = 'decicentillion', [112] = 'undecicentillion', [121] = 'viginticentillion', [122] = 'unviginticentillion', [131] = 'trigintacentillion', [141] = 'quadragintacentillion', [151] = 'quinquagintacentillion', [161] = 'sexagintacentillion', [171] = 'septuagintacentillion', [181] = 'octogintacentillion', [191] = 'nonagintacentillion', [201] = 'ducentillion', [301] = 'trecentillion', [401] = 'quadringentillion', [501] = 'quingentillion', [601] = 'sescentillion', [701] = 'septingentillion', [801] = 'octingentillion', [901] = 'nongentillion', [1001] = 'millinillion', } local roman_numerals = { I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000 } local engord_tens_end = { ['twentieth'] = 20, ['thirtieth'] = 30, ['fortieth'] = 40, ['fiftieth'] = 50, ['sixtieth'] = 60, ['seventieth'] = 70, ['eightieth'] = 80, ['ninetieth'] = 90, } local eng_tens_cont = { ['twenty'] = 20, ['thirty'] = 30, ['forty'] = 40, ['fifty'] = 50, ['sixty'] = 60, ['seventy'] = 70, ['eighty'] = 80, ['ninety'] = 90, } -- Converts a given valid roman numeral (and some invalid roman numerals) to a number. Returns { -1, errorstring } on error. local function roman_to_numeral(roman) if type(roman) ~= "string" then return -1, "roman numeral not a string" end local rev = roman:reverse() local raising = true local last = 0 local result = 0 for i = 1, #rev do local c = rev:sub(i, i) local next = roman_numerals[c] if next == nil then return -1, "roman numeral contains illegal character " .. c end if next > last then result = result + next raising = true elseif next < last then result = result - next raising = false elseif raising then result = result + next else result = result - next end last = next end return result end -- Converts a given integer between 0 and 100 to English text (e.g. 47 -> forty-seven). local function numeral_to_english_less_100(num, ordinal, plural, zero) local terminal_ones, terminal_tens if ordinal then terminal_ones = ones_position_ord terminal_tens = tens_position_ord elseif plural then terminal_ones = ones_position_plural terminal_tens = tens_position_plural else terminal_ones = ones_position terminal_tens = tens_position end if num == 0 and zero ~= nil then return zero elseif num < 20 then return terminal_ones[num] elseif num % 10 == 0 then return terminal_tens[num / 10] else return tens_position[math.floor(num / 10)] .. '-' .. terminal_ones[num % 10] end end local function standard_suffix(ordinal, plural) if ordinal then return 'th' end if plural then return 's' end return '' end -- Converts a given integer (in string form) between 0 and 1000 to English text (e.g. 47 -> forty-seven). local function numeral_to_english_less_1000(num, use_and, ordinal, plural, zero) num = tonumber(num) if num < 100 then return numeral_to_english_less_100(num, ordinal, plural, zero) elseif num % 100 == 0 then return ones_position[num/100] .. ' hundred' .. standard_suffix(ordinal, plural) else return ones_position[math.floor(num/100)] .. ' hundred ' .. (use_and and 'and ' or '') .. numeral_to_english_less_100(num % 100, ordinal, plural, zero) end end -- Converts an ordinal in English text from 'zeroth' to 'ninety-ninth' inclusive to a number [0–99], else -1. local function english_to_ordinal(english) local eng = string.lower(english or '') local engord_lt20 = {} -- ones_position_ord{} keys & values swapped for k, v in pairs( ones_position_ord ) do engord_lt20[v] = k end if engord_lt20[eng] then return engord_lt20[eng] -- e.g. first -> 1 elseif engord_tens_end[eng] then return engord_tens_end[eng] -- e.g. ninetieth -> 90 else local tens, ones = string.match(eng, '^([a-z]+)[%s%-]+([a-z]+)$') if tens and ones then local tens_cont = eng_tens_cont[tens] local ones_end = engord_lt20[ones] if tens_cont and ones_end then return tens_cont + ones_end -- e.g. ninety-ninth -> 99 end end end return -1 -- Failed end -- Converts a number in English text from 'zero' to 'ninety-nine' inclusive to a number [0–99], else -1. local function english_to_numeral(english) local eng = string.lower(english or '') local eng_lt20 = { ['single'] = 1 } -- ones_position{} keys & values swapped for k, v in pairs( ones_position ) do eng_lt20[v] = k end if eng_lt20[eng] then return eng_lt20[eng] -- e.g. one -> 1 elseif eng_tens_cont[eng] then return eng_tens_cont[eng] -- e.g. ninety -> 90 else local tens, ones = string.match(eng, '^([a-z]+)[%s%-]+([a-z]+)$') if tens and ones then local tens_cont = eng_tens_cont[tens] local ones_end = eng_lt20[ones] if tens_cont and ones_end then return tens_cont + ones_end -- e.g. ninety-nine -> 99 end end end return -1 -- Failed end -- Converts a number expressed as a string in scientific notation to a string in standard decimal notation -- e.g. 1.23E5 -> 123000, 1.23E-5 = .0000123. Conversion is exact, no rounding is performed. local function scientific_notation_to_decimal(num) local exponent, subs = num:gsub("^%-?%d*%.?%d*%-?[Ee]([+%-]?%d+)$", "%1") if subs == 0 then return num end -- Input not in scientific notation, just return unmodified exponent = tonumber(exponent) local negative = num:find("^%-") local _, decimal_pos = num:find("%.") -- Mantissa will consist of all decimal digits with no decimal point local mantissa = num:gsub("^%-?(%d*)%.?(%d*)%-?[Ee][+%-]?%d+$", "%1%2") if negative and decimal_pos then decimal_pos = decimal_pos - 1 end if not decimal_pos then decimal_pos = #mantissa + 1 end -- Remove leading zeros unless decimal point is in first position while decimal_pos > 1 and mantissa:sub(1,1) == '0' do mantissa = mantissa:sub(2) decimal_pos = decimal_pos - 1 end -- Shift decimal point right for exponent > 0 while exponent > 0 do decimal_pos = decimal_pos + 1 exponent = exponent - 1 if decimal_pos > #mantissa + 1 then mantissa = mantissa .. '0' end -- Remove leading zeros unless decimal point is in first position while decimal_pos > 1 and mantissa:sub(1,1) == '0' do mantissa = mantissa:sub(2) decimal_pos = decimal_pos - 1 end end -- Shift decimal point left for exponent < 0 while exponent < 0 do if decimal_pos == 1 then mantissa = '0' .. mantissa else decimal_pos = decimal_pos - 1 end exponent = exponent + 1 end -- Insert decimal point in correct position and return return (negative and '-' or '') .. mantissa:sub(1, decimal_pos - 1) .. '.' .. mantissa:sub(decimal_pos) end -- Rounds a number to the nearest integer (NOT USED) local function round_num(x) if x%1 >= 0.5 then return math.ceil(x) else return math.floor(x) end end -- Rounds a number to the nearest two-word number (round = up, down, or "on" for round to nearest). -- Numbers with two digits before the decimal will be rounded to an integer as specified by round. -- Larger numbers will be rounded to a number with only one nonzero digit in front and all other digits zero. -- Negative sign is preserved and does not count towards word limit. local function round_for_english(num, round) -- If an integer with at most two digits, just return if num:find("^%-?%d?%d%.?$") then return num end local negative = num:find("^%-") if negative then -- We're rounding magnitude so flip it if round == 'up' then round = 'down' elseif round == 'down' then round = 'up' end end -- If at most two digits before decimal, round to integer and return local _, _, small_int, trailing_digits, round_digit = num:find("^%-?(%d?%d?)%.((%d)%d*)$") if small_int then if small_int == '' then small_int = '0' end if (round == 'up' and trailing_digits:find('[1-9]')) or (round == 'on' and tonumber(round_digit) >= 5) then small_int = tostring(tonumber(small_int) + 1) end return (negative and '-' or '') .. small_int end -- When rounding up, any number with > 1 nonzero digit will round up (e.g. 1000000.001 rounds up to 2000000) local nonzero_digits = 0 for digit in num:gfind("[1-9]") do nonzero_digits = nonzero_digits + 1 end num = num:gsub("%.%d*$", "") -- Remove decimal part -- Second digit used to determine which way to round lead digit local _, _, lead_digit, round_digit, round_digit_2, rest = num:find("^%-?(%d)(%d)(%d)(%d*)$") if tonumber(lead_digit .. round_digit) < 20 and (1 + #rest) % 3 == 0 then -- In English numbers < 20 are one word so put 2 digits in lead and round based on 3rd lead_digit = lead_digit .. round_digit round_digit = round_digit_2 else rest = round_digit_2 .. rest end if (round == 'up' and nonzero_digits > 1) or (round == 'on' and tonumber(round_digit) >= 5) then lead_digit = tostring(tonumber(lead_digit) + 1) end -- All digits but lead digit will turn to zero rest = rest:gsub("%d", "0") return (negative and '-' or '') .. lead_digit .. '0' .. rest end local denominators = { [2] = { 'half', plural = 'halves' }, [3] = { 'third' }, [4] = { 'quarter', us = 'fourth' }, [5] = { 'fifth' }, [6] = { 'sixth' }, [8] = { 'eighth' }, [9] = { 'ninth' }, [10] = { 'tenth' }, [16] = { 'sixteenth' }, } -- Return status, fraction where: -- status is a string: -- "finished" if there is a fraction with no whole number; -- "ok" if fraction is empty or valid; -- "unsupported" if bad fraction; -- fraction is a string giving (numerator / denominator) as English text, or is "". -- Only unsigned fractions with a very limited range of values are supported, -- except that if whole is empty, the numerator can use "-" to indicate negative. -- whole (string or nil): nil or "" if no number before the fraction -- numerator (string or nil): numerator, if any (default = 1 if a denominator is given) -- denominator (string or nil): denominator, if any -- sp_us (boolean): true if sp=us -- negative_word (string): word to use for negative sign, if whole is empty -- use_one (boolean): false: 2+1/2 → "two and a half"; true: "two and one-half" local function fraction_to_english(whole, numerator, denominator, sp_us, negative_word, use_one) if numerator or denominator then local finished = (whole == nil or whole == '') local sign = '' if numerator then if finished and numerator:sub(1, 1) == '-' then numerator = numerator:sub(2) sign = negative_word .. ' ' end else numerator = '1' end if not numerator:match('^%d+$') or not denominator or not denominator:match('^%d+$') then return 'unsupported', '' end numerator = tonumber(numerator) denominator = tonumber(denominator) local dendata = denominators[denominator] if not (dendata and 1 <= numerator and numerator <= 99) then return 'unsupported', '' end local numstr, denstr local sep = '-' if numerator == 1 then denstr = sp_us and dendata.us or dendata[1] if finished or use_one then numstr = 'one' elseif denstr:match('^[aeiou]') then numstr = 'an' sep = ' ' else numstr = 'a' sep = ' ' end else numstr = numeral_to_english_less_100(numerator) denstr = dendata.plural if not denstr then denstr = (sp_us and dendata.us or dendata[1]) .. 's' end end if finished then return 'finished', sign .. numstr .. sep .. denstr end return 'ok', ' and ' .. numstr .. sep .. denstr end return 'ok', '' end -- Takes a decimal number and converts it to English text. -- Return nil if a fraction cannot be converted (only some numbers are supported for fractions). -- num (string or nil): the number to convert. -- Can be an arbitrarily large decimal, such as "-123456789123456789.345", and -- can use scientific notation (e.g. "1.23E5"). -- May fail for very large numbers not listed in "groups" such as "1E4000". -- num is nil if there is no whole number before a fraction. -- numerator (string or nil): numerator of fraction (nil if no fraction) -- denominator (string or nil): denominator of fraction (nil if no fraction) -- capitalize (boolean): whether to capitalize the result (e.g. 'One' instead of 'one') -- use_and (boolean): whether to use the word 'and' between tens/ones place and higher places -- hyphenate (boolean): whether to hyphenate all words in the result, useful as an adjective -- ordinal (boolean): whether to produce an ordinal (e.g. 'first' instead of 'one') -- plural (boolean): whether to pluralize the resulting number -- links: nil: do not add any links; 'on': link "billion" and larger to Orders of magnitude article; -- any other text: list of numbers to link (e.g. "billion,quadrillion") -- negative_word: word to use for negative sign (typically 'negative' or 'minus'; nil to use default) -- round: nil or '': no rounding; 'on': round to nearest two-word number; 'up'/'down': round up/down to two-word number -- zero: word to use for value '0' (nil to use default) -- use_one (boolean): false: 2+1/2 → "two and a half"; true: "two and one-half" local function _numeral_to_english(num, numerator, denominator, capitalize, use_and, hyphenate, ordinal, plural, links, negative_word, round, zero, use_one) if not negative_word then if use_and then -- TODO Should 'minus' be used when do not have sp=us? -- If so, need to update testcases, and need to fix "minus zero". -- negative_word = 'minus' negative_word = 'negative' else negative_word = 'negative' end end local status, fraction_text = fraction_to_english(num, numerator, denominator, not use_and, negative_word, use_one) if status == 'unsupported' then return nil end if status == 'finished' then -- Input is a fraction with no whole number. -- Hack to avoid executing stuff that depends on num being a number. local s = fraction_text if hyphenate then s = s:gsub("%s", "-") end if capitalize then s = s:gsub("^%l", string.upper) end return s end num = scientific_notation_to_decimal(num) if round and round ~= '' then if round ~= 'on' and round ~= 'up' and round ~= 'down' then error("Invalid rounding mode") end num = round_for_english(num, round) end -- Separate into negative sign, num (digits before decimal), decimal_places (digits after decimal) local MINUS = '−' -- Unicode U+2212 MINUS SIGN (may be in values from [[Module:Convert]]) if num:sub(1, #MINUS) == MINUS then num = '-' .. num:sub(#MINUS + 1) -- replace MINUS with '-' elseif num:sub(1, 1) == '+' then num = num:sub(2) -- ignore any '+' end local negative = num:find("^%-") local decimal_places, subs = num:gsub("^%-?%d*%.(%d+)$", "%1") if subs == 0 then decimal_places = nil end num, subs = num:gsub("^%-?(%d*)%.?%d*$", "%1") if num == '' and decimal_places then num = '0' end if subs == 0 or num == '' then error("Invalid decimal numeral") end -- For each group of 3 digits except the last one, print with appropriate group name (e.g. million) local s = '' while #num > 3 do if s ~= '' then s = s .. ' ' end local group_num = math.floor((#num - 1) / 3) local group = groups[group_num] local group_digits = #num - group_num*3 s = s .. numeral_to_english_less_1000(num:sub(1, group_digits), false, false, false, zero) .. ' ' if links and (((links == 'on' and group_num >= 3) or links:find(group)) and group_num <= 13) then s = s .. '[[Orders_of_magnitude_(numbers)#10' .. group_num*3 .. '|' .. group .. ']]' else s = s .. group end num = num:sub(1 + group_digits) num = num:gsub("^0*", "") -- Trim leading zeros end -- Handle final three digits of integer part if s ~= '' and num ~= '' then if #num <= 2 and use_and then s = s .. ' and ' else s = s .. ' ' end end if s == '' or num ~= '' then s = s .. numeral_to_english_less_1000(num, use_and, ordinal, plural, zero) elseif ordinal or plural then -- Round numbers like "one million" take standard suffixes for ordinal/plural s = s .. standard_suffix(ordinal, plural) end -- For decimal places (if any) output "point" followed by spelling out digit by digit if decimal_places then s = s .. ' point' for i = 1, #decimal_places do s = s .. ' ' .. ones_position[tonumber(decimal_places:sub(i,i))] end end s = s:gsub("^%s*(.-)%s*$", "%1") -- Trim whitespace if ordinal and plural then s = s .. 's' end -- s suffix works for all ordinals if negative and s ~= zero then s = negative_word .. ' ' .. s end s = s:gsub("negative zero", "zero") s = s .. fraction_text if hyphenate then s = s:gsub("%s", "-") end if capitalize then s = s:gsub("^%l", string.upper) end return s end local function _numeral_to_english2(args) local num = tostring(args.num) num = num:gsub("^%s*(.-)%s*$", "%1") -- Trim whitespace num = num:gsub(",", "") -- Remove commas num = num:gsub("^<span[^<>]*></span>", "") -- Generated by Template:age if num ~= '' then -- a fraction may have an empty whole number if not num:find("^%-?%d*%.?%d*%-?[Ee]?[+%-]?%d*$") then -- Input not in a valid format, try to eval it as an expr to see -- if that produces a number (e.g. "3 + 5" will become "8"). local noerr, result = pcall(mw.ext.ParserFunctions.expr, num) if noerr then num = result end end end -- Call helper function passing args return _numeral_to_english( num, args['numerator'], args['denominator'], args['capitalize'], args['use_and'], args['hyphenate'], args['ordinal'], args['plural'], args['links'], args['negative_word'], args['round'], args['zero'], args['use_one'] ) or '' end local p = { -- Functions that can be called from another module roman_to_numeral = roman_to_numeral, spell_number = _numeral_to_english, spell_number2 = _numeral_to_english2, english_to_ordinal = english_to_ordinal, english_to_numeral = english_to_numeral, } function p._roman_to_numeral(frame) -- Callable via {{#invoke:ConvertNumeric|_roman_to_numeral|VI}} return roman_to_numeral(frame.args[1]) end function p._english_to_ordinal(frame) -- callable via {{#invoke:ConvertNumeric|_english_to_ordinal|First}} return english_to_ordinal(frame.args[1]) end function p._english_to_numeral(frame) -- callable via {{#invoke:ConvertNumeric|_english_to_numeral|One}} return english_to_numeral(frame.args[1]) end function p.numeral_to_english(frame) local args = frame.args -- Tail call to helper function passing args from frame return _numeral_to_english2{ ['num'] = args[1], ['numerator'] = args['numerator'], ['denominator'] = args['denominator'], ['capitalize'] = args['case'] == 'U' or args['case'] == 'u', ['use_and'] = args['sp'] ~= 'us', ['hyphenate'] = args['adj'] == 'on', ['ordinal'] = args['ord'] == 'on', ['plural'] = args['pl'] == 'on', ['links'] = args['lk'], ['negative_word'] = args['negative'], ['round'] = args['round'], ['zero'] = args['zero'], ['use_one'] = args['one'] == 'one' -- experiment: using '|one=one' makes fraction 2+1/2 give "two and one-half" instead of "two and a half" } end ---- recursive function for p.decToHex local function decToHexDigit(dec) local dig = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"} local div = math.floor(dec/16) local mod = dec-(16*div) if div >= 1 then return decToHexDigit(div)..dig[mod+1] else return dig[mod+1] end end -- I think this is supposed to be done with a tail call but first I want something that works at all ---- finds all the decimal numbers in the input text and hexes each of them function p.decToHex(frame) local args=frame.args local parent=frame.getParent(frame) local pargs={} if parent then pargs=parent.args end local text=args[1] or pargs[1] or "" local minlength=args.minlength or pargs.minlength or 1 minlength=tonumber(minlength) local prowl=mw.ustring.gmatch(text,"(.-)(%d+)") local output="" repeat local chaff,dec=prowl() if not(dec) then break end local hex=decToHexDigit(dec) while (mw.ustring.len(hex)<minlength) do hex="0"..hex end output=output..chaff..hex until false local chaff=mw.ustring.match(text,"(%D+)$") or "" return output..chaff end return p dbb876f8710ee1407b421a28b3e9bc5a6ea36cda Module:Tag 828 508 1023 1022 2023-07-10T16:51:44Z BigTa1k 2 1 revision imported wikitext text/x-wiki <code class="{{#ifeq:{{{wrap|}}}|yes|wrap|nowrap}}" style="{{#ifeq:{{{style|}}}|plain|border:none;background:transparent;|{{{style|}}}}}"><!-- Opening tag -->{{#switch:{{{2|pair}}} |c|close = |e|empty|s|single|v|void |o|open |p|pair = &lt;{{#if:{{{link|}}}|[[HTML element#{{{1|tag}}}|{{{1|tag}}}]]|{{{1|tag}}}}}{{#if:{{{params|{{{attribs|}}}}}}|&#32;{{{params|{{{attribs}}}}}}}} }}<!-- Content between tags -->{{#switch:{{{2|pair}}} |c|close = {{{content|}}} |e|empty|s|single|v|void = &#32;&#47;&gt; |o|open = &gt;{{{content|}}} |p|pair = {{#ifeq:{{{1|tag}}}|!--||&gt;}}{{{content|...}}} }}<!-- Closing tag -->{{#switch:{{{2|pair}}} |e|empty|s|single|v|void |o|open = |c|close |p|pair = {{#ifeq:{{{1|tag}}}|!--|--&gt;|&lt;&#47;{{{1|tag}}}&gt;}} }}<!-- --></code><noinclude> {{Documentation}} </noinclude> eae208bc1612c834de697fa3ee9b343966cf8602 Module:(( 828 509 1025 1024 2023-07-10T16:51:45Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{<noinclude> {{documentation}} </noinclude> 3f1af626705a06bc9cdea766736e292d321af5a3 Module:)) 828 510 1027 1026 2023-07-10T16:51:45Z BigTa1k 2 1 revision imported wikitext text/x-wiki }}<noinclude> {{documentation}} </noinclude> 85ca77d4d6ff71d8e6396ebd798f87fa7f45dc02 Module:String split 828 511 1029 1028 2023-07-10T16:51:46Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{safesubst<noinclude/>:#invoke:String2 |split}}<noinclude> {{documentation}} </noinclude> fe0e5c984dbf2d0cf923f385a3b2596010475bc6 Module:Format item 828 512 1031 1030 2023-07-10T16:51:46Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly>{{#invoke:Item|format}}</includeonly><noinclude> {{doc}} </noinclude> 0de34fe93b7513b034123ce1c532fd0f2df896dc Module:Item 828 513 1033 1032 2023-07-10T16:51:46Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly>{{#invoke:Item|pack}}</includeonly><noinclude> {{doc}} </noinclude> 2a8d49a45be839260ad83d33fa73c804d0a39e55 1039 1033 2023-07-10T16:51:47Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly>{{#invoke:Item|pack}}</includeonly><noinclude> {{doc}} </noinclude> 2a8d49a45be839260ad83d33fa73c804d0a39e55 Module:Markup 828 514 1035 1034 2023-07-10T16:51:46Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly>{| style="{{#if:{{{width}}}|width:{{{width}}};}} margin-top:0;margin-left:{{{margin-left|{{{margin|0}}}}}}; border-width:medium; padding:0; {{{style|}}}" {{#if:{{{title|}}} |{{!}}+ '''{{{title}}}'''}} {{#if:{{{noheaders|}}}{{{notitle|}}} | |! scope="col" style="width:50%;{{{colheaderstyle|}}}{{{col1headerstyle|}}}"{{!}} {{{col1|{{{t1|Markup}}}}}} {{!!}}<!-- -->scope="col" style="width:50%;{{{colheaderstyle|}}}{{{col2headerstyle|}}}"{{!}} {{{col2|{{{t2|Renders as}}}}}} }}<!-- -->{{For loop||call=format item|pc1n=template|pc1v=Markup/row|pc2n=c1style|pc2v={{{col1style|{{{codestyle|{{{markupstyle|}}}}}}}}}|pc3n=c2style|pc3v={{{col2style|{{{outputstyle|}}}}}}|pv=item|{{item|c1={{{1|}}}|c2={{{2|}}}}}|{{item|c1={{{3|}}}|c2={{{4|}}}}}|{{item|c1={{{5|}}}|c2={{{6|}}}}}|{{item|c1={{{7|}}}|c2={{{8|}}}}}|{{item|c1={{{9|}}}|c2={{{10|}}}}}|{{item|c1={{{11|}}}|c2={{{12|}}}}}|{{item|c1={{{13|}}}|c2={{{14|}}}}}|{{item|c1={{{15|}}}|c2={{{16|}}}}}|{{item|c1={{{17|}}}|c2={{{18|}}}}}|{{item|c1={{{19|}}}|c2={{{20|}}}}}|{{item|c1={{{21|}}}|c2={{{22|}}}}}|{{item|c1={{{23|}}}|c2={{{24|}}}}}|{{item|c1={{{25|}}}|c2={{{26|}}}}}|{{item|c1={{{27|}}}|c2={{{28|}}}}}|{{item|c1={{{29|}}}|c2={{{30|}}}}}|{{item|c1={{{31|}}}|c2={{{32|}}}}}|{{item|c1={{{33|}}}|c2={{{34|}}}}}|{{item|c1={{{35|}}}|c2={{{36|}}}}}|{{item|c1={{{37|}}}|c2={{{38|}}}}}|{{item|c1={{{39|}}}|c2={{{40|}}}}}}} |- |style="border-width:1px;border-style: none none none;border-color:#ddd; padding:5px; vertical-align:top;"| |style="border-width:1px;border-style: none none none;border-color:#ddd; padding:5px; vertical-align:top;"| |}</includeonly><noinclude> {{Documentation}} </noinclude> deb7b37c29df73fd7da8dd882ed02722686c63a7 Module:Markup/row 828 515 1037 1036 2023-07-10T16:51:47Z BigTa1k 2 1 revision imported wikitext text/x-wiki <noinclude>{| style="border-width:medium;"</noinclude><includeonly>{{#if:{{{c1|value}}}|&#32; {{!-}} {{!}}style{{=}}"background:#f8f9fa ; border:1px solid #eaecf0 ; padding:9px 5px 5px; vertical-align:top;"{{!}} <!-- -->{{#tag:pre |{{{c1|[markup]}}}|style="margin:0;border:none;padding:0; word-wrap:break-word; white-space:-moz-pre-wrap;white-space:-o-pre-wrap;white-space:-pre-wrap;white-space:pre-wrap; {{{c1style|}}}"}} {{!}}style{{=}}"background:#f8f9fa ; border:1px solid #eaecf0 ; padding:5px; vertical-align:top;"{{!}} <!-- --><div style="{{{c2style|}}}"> {{{c2|[''rendering'']}}} </div> }}</includeonly><!-- --><noinclude> |- |style="border-width:1px;border-style:solid none none none;border-color:#ddd; padding:5px; vertical-align:text-top;"| |style="border-width:1px;border-style:solid none none none;border-color:#ddd; padding:5px; vertical-align:text-top;"| |- |}</noinclude> 2636425c7478cebf61528d5eb4cab52b8e695ec9 Module:Lx 828 516 1041 1040 2023-07-10T16:51:48Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly><span class="plainlinks nourlexpansion lx"><!-- -->[[{{{1}}}{{{2}}}]]&nbsp;<!-- --><span style="font-size:90%;">(<!-- -->{{#if:{{{noedit|}}}||[{{fullurl:{{{1}}}{{{2}}}|action=edit}} edit] &#124;}} <!-- -->{{#if:{{{notalk|}}}||[[{{{3}}}{{{5|:}}}{{{2}}}|{{{4}}}]] &#124;}} <!-- -->{{#if:{{{nohistory|}}}||[{{fullurl:{{{1}}}{{{2}}}|action=history}} history] &#124;}} <!-- -->{{#if:{{{nolinks|}}}||[{{fullurl:Special:Whatlinkshere/{{{1}}}{{{2}}}}} links] &#124;}} <!-- -->{{#if:{{{nowatch|}}}||[{{fullurl:{{{1}}}{{{2}}}|action=watch}} watch] &#124;}} <!-- -->{{#if:{{{nologs|}}}||[{{fullurl:Special:Log|page={{urlencode:{{{1}}}{{{2}}}}}}} logs]}}<!-- -->)</span></span><!-- --></includeonly><noinclude> {{Documentation}} </noinclude> 0ff233c8941d4624bed30222e0d2ae8ac142c899 Module:One2a 828 517 1043 1042 2023-07-10T16:51:48Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{#invoke:String2 |one2a}}<noinclude> {{documentation}} </noinclude> bc03f614684eff759042f705fc94764db994e860 Module:Ml 828 518 1045 1044 2023-07-10T16:51:49Z BigTa1k 2 1 revision imported wikitext text/x-wiki #REDIRECT [[Template:Module link]] {{Redirect category shell| {{R from move}} }} 55769c1a9d3b6c6828298b7a402d8e8506532cdd Module:Module link 828 519 1047 1046 2023-07-10T16:51:50Z BigTa1k 2 1 revision imported wikitext text/x-wiki &#123;&#123;{{{{{|safesubst:}}}#invoke:Separated entries|main|[[Module:{{{1}}}{{{section|}}}|#invoke:{{{1}}}]]|{{{2|''function''}}}|separator=&#124;}}&#125;&#125;<noinclude> {{documentation}} <!-- Categories go on the /doc subpage and interwikis go on Wikidata. --> </noinclude> 4dbda5bd7aa68e983beca7d836c547f8d146f595 Module:ParserFunction 828 520 1049 1048 2023-07-10T16:51:51Z BigTa1k 2 1 revision imported wikitext text/x-wiki <includeonly>{{#if:{{{_code|}}}|<code>}}{{((}}{{ #switch: {{lc: {{{1|if}}} }} | expr | if | ifeq | iferror | ifexpr | ifexist | rel2abs | switch | time | titleparts = [[mw:Help:Extension:ParserFunctions#.23{{lc:{{{1|if}}}}}|#{{{1|if}}}]] | lc | uc | lcfirst | ucfirst | urlencode | anchorencode | ns = [[meta:Help:Parser function#{{uc:{{{1}}}}}|{{{1}}}]] | localurl | localurle | fullurl | fullurle = [[meta:Help:Parser function#URLs etc.|{{{1}}}]] | language = [[meta:Help:Parser function#.23{{lc:{{{1}}}}}:|#{{{1}}}]] | pagesincategory | pagesincat = [[meta:Help:Parser function#Pages in category|{{{1}}}]] | section | section-x | section-h | lst | lstx | lsth = [[Help:Labeled section transclusion|#{{{1}}}]] | #default = [[H:MW#{{{1}}}|{{{1}}}]] }}{{ #if: {{{2|}}} | {{colon}}{{{2|}}} }}{{ #if: {{{3|}}} | {{ #ifeq: {{{2|}}} | | {{colon}}{{!}} }}{{!}}{{{3}}} }}{{ #if: {{{4|}}} | {{ #ifeq: {{{2|}}} | | {{colon}}{{!}} }}{{ #ifeq: {{{3|}}} | | {{!}} }}{{!}}{{{4}}} }}{{ #if: {{{5|}}} | {{ #ifeq: {{{2|}}} | | {{colon}}{{!}} }}{{ #ifeq: {{{3|}}} | | {{!}} }}{{ #ifeq: {{{4|}}} | | {{!}} }}{{!}}{{{5}}} }}{{))}}{{#if:{{{_code|}}}|</code>}}</includeonly><noinclude> {{documentation}} </noinclude> be6f87cefa60887c80fcf5809a9c95d2050cde70 Module:Transclusion count/data/S 828 521 1051 1050 2023-07-10T16:51:51Z BigTa1k 2 1 revision imported Scribunto text/plain return { ["S"] = 3500, ["S-aca"] = 6300, ["S-ach"] = 16000, ["S-aft"] = 217000, ["S-aft/filter"] = 217000, ["S-bef"] = 222000, ["S-bef/filter"] = 222000, ["S-break"] = 5000, ["S-civ"] = 2600, ["S-dip"] = 5300, ["S-end"] = 245000, ["S-gov"] = 7800, ["S-hon"] = 3800, ["S-hou"] = 9500, ["S-inc"] = 13000, ["S-legal"] = 9300, ["S-mil"] = 12000, ["S-new"] = 15000, ["S-non"] = 9300, ["S-npo"] = 4000, ["S-off"] = 40000, ["S-par"] = 50000, ["S-par/en"] = 3200, ["S-par/gb"] = 3300, ["S-par/uk"] = 11000, ["S-par/us-hs"] = 11000, ["S-par/us-sen"] = 2000, ["S-ppo"] = 13000, ["S-prec"] = 3200, ["S-rail"] = 6300, ["S-rail-start"] = 6200, ["S-rail/lines"] = 6300, ["S-reg"] = 20000, ["S-rel"] = 18000, ["S-roy"] = 2700, ["S-s"] = 3600, ["S-sports"] = 10000, ["S-start"] = 239000, ["S-ttl"] = 229000, ["S-vac"] = 5900, ["SCO"] = 3700, ["SDcat"] = 5340000, ["SECOND"] = 2300, ["SGP"] = 2600, ["SIA"] = 2600, ["SIPA"] = 2500, ["SLO"] = 4200, ["SMS"] = 7200, ["SMU"] = 2100, ["SPI_archive_notice"] = 70000, ["SPIarchive_notice"] = 70000, ["SPIcat"] = 3800, ["SPIclose"] = 3300, ["SPIpriorcases"] = 64000, ["SR/Olympics_profile"] = 3200, ["SRB"] = 3600, ["SS"] = 20000, ["SSPa"] = 2600, ["STN"] = 12000, ["SUBJECTSPACE_formatted"] = 42000, ["SUI"] = 8400, ["SVG"] = 3300, ["SVG-Logo"] = 17000, ["SVG-Res"] = 15000, ["SVG-logo"] = 2900, ["SVK"] = 5900, ["SVN"] = 5100, ["SWE"] = 12000, ["Sandbox_other"] = 228000, ["Saturday"] = 2600, ["Saved_book"] = 52000, ["Sc"] = 2800, ["Scholia"] = 2700, ["School_block"] = 13000, ["School_disambiguation"] = 3300, ["Schoolblock"] = 6700, ["Schooldis"] = 2700, ["Schoolip"] = 10000, ["Scientist_icon"] = 15000, ["Scientist_icon2"] = 15000, ["Sclass"] = 31000, ["Sclass2"] = 10000, ["Screen_reader-only"] = 43000, ["Screen_reader-only/styles.css"] = 43000, ["Script"] = 5800, ["Script/Arabic"] = 2100, ["Script/Hebrew"] = 4700, ["Script/Nastaliq"] = 14000, ["Script/doc/id-unk"] = 2900, ["Script/doc/id-unk/core"] = 2900, ["Script/doc/id-unk/is-iso-alpha4"] = 2800, ["Script/doc/id-unk/name-to-alpha4"] = 2900, ["Script/styles.css"] = 3000, ["Script/styles_arabic.css"] = 2100, ["Script/styles_hebrew.css"] = 4700, ["Sdash"] = 3000, ["Search_box"] = 49000, ["Search_link"] = 9600, ["Section_link"] = 51000, ["Section_sizes"] = 2300, ["See"] = 11000, ["See_also"] = 182000, ["Seealso"] = 6700, ["Select_skin"] = 4300, ["Selected_article"] = 2700, ["Selected_picture"] = 2500, ["Self"] = 50000, ["Self-published_inline"] = 3900, ["Self-published_source"] = 6600, ["Self-reference"] = 2700, ["Self-reference_tool"] = 5000, ["Self/migration"] = 34000, ["Self2"] = 2100, ["Sent_off"] = 13000, ["Sentoff"] = 4100, ["Separated_entries"] = 174000, ["Sequence"] = 3700, ["Serial_killer_opentask"] = 3600, ["Series_overview"] = 7600, ["Serif"] = 2800, ["Set_category"] = 35000, ["Set_index_article"] = 5700, ["Sets_taxobox_colour"] = 93000, ["Sfn"] = 151000, ["SfnRef"] = 132000, ["Sfnm"] = 3400, ["Sfnp"] = 17000, ["Sfnref"] = 10000, ["Sfrac"] = 4200, ["Sfrac/styles.css"] = 4200, ["SharedIPEDU"] = 3200, ["Shared_IP"] = 11000, ["Shared_IP_advice"] = 16000, ["Shared_IP_corp"] = 5000, ["Shared_IP_edu"] = 126000, ["Shared_IP_gov"] = 3000, ["Sharedip"] = 3300, ["Sharedipedu"] = 3600, ["Sherdog"] = 2600, ["Ship"] = 85000, ["Ship/maintenancecategory"] = 85000, ["Ship_index"] = 7000, ["Shipboxflag"] = 20000, ["Shipboxflag/core"] = 20000, ["Shipwrecks_navbox_footer"] = 9900, ["Shipwrecks_navbox_footer/link"] = 9900, ["Short_description"] = 5450000, ["Short_description/lowercasecheck"] = 5450000, ["Short_pages_monitor"] = 10000, ["Short_pages_monitor/maximum_length"] = 10000, ["Shortcut"] = 19000, ["Should_be_SVG"] = 9100, ["Show_button"] = 2130000, ["Sic"] = 32000, ["Sica"] = 3000, ["Side_box"] = 1110000, ["Sidebar"] = 252000, ["Sidebar_games_events"] = 36000, ["Sidebar_person"] = 2300, ["Sidebar_person/styles.css"] = 2300, ["Sidebar_with_collapsible_lists"] = 92000, ["Sigfig"] = 3700, ["Significant_figures"] = 5000, ["Significant_figures/rnd"] = 4600, ["Signpost-subscription"] = 2100, ["Sildb_prim"] = 2000, ["Silver02"] = 16000, ["Silver2"] = 47000, ["Silver_medal"] = 5400, ["Similar_names"] = 2100, ["Single+double"] = 6700, ["Single+space"] = 14000, ["Single-innings_cricket_match"] = 3200, ["Single_chart"] = 37000, ["Single_chart/chartnote"] = 37000, ["Single_namespace"] = 199000, ["Singlechart"] = 20000, ["Singles"] = 41000, ["Sister-inline"] = 186000, ["Sister_project"] = 1040000, ["Sister_project_links"] = 10000, ["Sisterlinks"] = 2900, ["Skip_to_talk"] = 12000, ["Skip_to_talk/styles.css"] = 12000, ["Sky"] = 2700, ["Sky/styles.css"] = 2700, ["Slink"] = 12000, ["Small"] = 594000, ["Small_Solar_System_bodies"] = 3600, ["Smallcaps"] = 17000, ["Smallcaps/styles.css"] = 18000, ["Smallcaps_all"] = 2900, ["Smalldiv"] = 21000, ["Smaller"] = 72000, ["Smallsup"] = 21000, ["Smiley"] = 43000, ["Snd"] = 155000, ["Snds"] = 6300, ["Soccer_icon"] = 130000, ["Soccer_icon2"] = 130000, ["Soccer_icon4"] = 5100, ["Soccerbase"] = 13000, ["Soccerbase_season"] = 6700, ["Soccerway"] = 74000, ["Sock"] = 46000, ["Sock_list"] = 3800, ["Sockcat"] = 2000, ["Sockmaster"] = 9300, ["Sockpuppet"] = 236000, ["Sockpuppet/altmaster"] = 2100, ["Sockpuppet/categorise"] = 236000, ["SockpuppetCheckuser"] = 5500, ["Sockpuppet_category"] = 45000, ["Sockpuppet_category/confirmed"] = 23000, ["Sockpuppet_category/suspected"] = 22000, ["Sockpuppetcheckuser"] = 3600, ["Sockpuppeteer"] = 24000, ["Soft_redirect"] = 6100, ["Soft_redirect_protection"] = 8200, ["Softredirect"] = 3200, ["Solar_luminosity"] = 4400, ["Solar_mass"] = 5200, ["Solar_radius"] = 4200, ["Soldier_icon"] = 3900, ["Soldier_icon2"] = 3900, ["Song"] = 8300, ["Songs"] = 19000, ["Songs_category"] = 8300, ["Songs_category/core"] = 8300, ["Sort"] = 115000, ["Sortname"] = 52000, ["Source-attribution"] = 27000, ["Source_check"] = 965000, ["Sourcecheck"] = 965000, ["Sources"] = 2400, ["South_America_topic"] = 2500, ["Sp"] = 250000, ["Space"] = 55000, ["Space+double"] = 18000, ["Space+single"] = 13000, ["Spaced_en_dash"] = 187000, ["Spaced_en_dash_space"] = 6400, ["Spaced_ndash"] = 23000, ["Spaces"] = 2680000, ["Spain_metadata_Wikidata"] = 7500, ["Spamlink"] = 13000, ["Species_Latin_name_abbreviation_disambiguation"] = 2200, ["Species_list"] = 15000, ["Speciesbox"] = 287000, ["Speciesbox/getGenus"] = 287000, ["Speciesbox/getSpecies"] = 287000, ["Speciesbox/name"] = 287000, ["Speciesbox/parameterCheck"] = 287000, ["Speciesbox/trim"] = 287000, ["Specieslist"] = 5300, ["Split_article"] = 3600, ["Spnd"] = 4000, ["Sport_icon"] = 14000, ["Sport_icon2"] = 15000, ["SportsYearCatUSstate"] = 6500, ["SportsYearCatUSstate/core"] = 6500, ["Sports_links"] = 64000, ["Sports_reference"] = 7400, ["Squad_maintenance"] = 3500, ["Sronly"] = 41000, ["Srt"] = 5000, ["Stack"] = 25000, ["Stack/styles.css"] = 34000, ["Stack_begin"] = 8900, ["Stack_end"] = 8900, ["StaleIP"] = 3200, ["Standings_table_end"] = 54000, ["Standings_table_entry"] = 54000, ["Standings_table_entry/record"] = 54000, ["Standings_table_start"] = 54000, ["Standings_table_start/colheader"] = 54000, ["Standings_table_start/colspan"] = 54000, ["Standings_table_start/styles.css"] = 54000, ["Starbox_astrometry"] = 5100, ["Starbox_begin"] = 5300, ["Starbox_catalog"] = 5200, ["Starbox_character"] = 5100, ["Starbox_detail"] = 5000, ["Starbox_end"] = 5200, ["Starbox_image"] = 2900, ["Starbox_observe"] = 5000, ["Starbox_reference"] = 5200, ["Start-Class"] = 64000, ["Start-date"] = 3800, ["Start_and_end_dates"] = 2600, ["Start_box"] = 8100, ["Start_date"] = 431000, ["Start_date_and_age"] = 136000, ["Start_date_and_years_ago"] = 6700, ["Start_of_course_timeline"] = 6000, ["Start_of_course_week"] = 6100, ["Start_tab"] = 4800, ["Startflatlist"] = 143000, ["Static_IP"] = 5900, ["Station"] = 7600, ["Station_link"] = 16000, ["Stdinchicite"] = 10000, ["Steady"] = 13000, ["Stl"] = 14000, ["Stn"] = 7200, ["Stn_art_lnk"] = 2000, ["Stnlnk"] = 30000, ["Storm_colour"] = 5100, ["Storm_path"] = 2100, ["StoryTeleplay"] = 3400, ["Str_endswith"] = 199000, ["Str_find"] = 115000, ["Str_index"] = 13000, ["Str_left"] = 4190000, ["Str_len"] = 18000, ["Str_letter"] = 175000, ["Str_letter/trim"] = 20000, ["Str_number"] = 8000, ["Str_number/trim"] = 169000, ["Str_rep"] = 310000, ["Str_right"] = 3290000, ["Str_trim"] = 5400, ["Str_≠_len"] = 34000, ["Str_≥_len"] = 71000, ["Strfind_short"] = 221000, ["Strikethrough"] = 16000, ["String_split"] = 2400, ["Strip_tags"] = 37000, ["Strong"] = 848000, ["Structurae"] = 2000, ["Stub-Class"] = 33000, ["Stub_Category"] = 13000, ["Stub_category"] = 18000, ["Stub_documentation"] = 36000, ["Student_editor"] = 27000, ["Student_sandbox"] = 4500, ["Student_table_row"] = 5100, ["Students_table"] = 5100, ["Su"] = 9800, ["Su-census1989"] = 4500, ["Sub"] = 4100, ["Subinfobox_bodystyle"] = 35000, ["Subject_bar"] = 18000, ["Suboff"] = 6100, ["Subon"] = 6200, ["Subpage_other"] = 282000, ["Subscription"] = 5800, ["Subscription_required"] = 34000, ["Subsidebar_bodystyle"] = 6700, ["Subst_only"] = 4600, ["Substituted_comment"] = 19000, ["Succession_box"] = 118000, ["Succession_links"] = 162000, ["Summer_Olympics_by_year_category_navigation"] = 2400, ["Summer_Olympics_by_year_category_navigation/core"] = 2400, ["Sunday"] = 2600, ["Sup"] = 58000, ["Superimpose2/base"] = 2100, ["Suppress_categories"] = 5100, ["Surname"] = 66000, ["Swiss_populations"] = 2400, ["Swiss_populations_NC"] = 3000, ["Swiss_populations_YM"] = 2300, ["Swiss_populations_ref"] = 2400, ["Switcher"] = 2500, ["Module:SDcat"] = 5340000, ["Module:SPI_archive_notice"] = 32000, ["Module:Science_redirect"] = 251000, ["Module:Science_redirect/conf"] = 251000, ["Module:Section_link"] = 51000, ["Module:Section_sizes"] = 3400, ["Module:See_also_if_exists"] = 72000, ["Module:Separated_entries"] = 2260000, ["Module:Series_overview"] = 7600, ["Module:Settlement_short_description"] = 709000, ["Module:Shortcut"] = 23000, ["Module:Shortcut/config"] = 23000, ["Module:Shortcut/styles.css"] = 23000, ["Module:Side_box"] = 1140000, ["Module:Side_box/styles.css"] = 1140000, ["Module:Sidebar"] = 334000, ["Module:Sidebar/configuration"] = 334000, ["Module:Sidebar/styles.css"] = 340000, ["Module:Sidebar_games_events"] = 36000, ["Module:Sidebar_games_events/styles.css"] = 36000, ["Module:Singles"] = 41000, ["Module:Sister_project_links"] = 14000, ["Module:Sister_project_links/bar/styles.css"] = 3200, ["Module:Sister_project_links/styles.css"] = 10000, ["Module:Sock_list"] = 3800, ["Module:Sort_title"] = 17000, ["Module:Sortkey"] = 196000, ["Module:Split_article"] = 3600, ["Module:Sports_career"] = 19000, ["Module:Sports_color"] = 68000, ["Module:Sports_color/baseball"] = 34000, ["Module:Sports_color/basketball"] = 22000, ["Module:Sports_color/ice_hockey"] = 3100, ["Module:Sports_rbr_table"] = 11000, ["Module:Sports_rbr_table/styles.css"] = 11000, ["Module:Sports_reference"] = 7400, ["Module:Sports_results"] = 14000, ["Module:Sports_results/styles.css"] = 9400, ["Module:Sports_table"] = 57000, ["Module:Sports_table/WDL"] = 50000, ["Module:Sports_table/WDL_OT"] = 2600, ["Module:Sports_table/WL"] = 3800, ["Module:Sports_table/argcheck"] = 57000, ["Module:Sports_table/styles.css"] = 57000, ["Module:Sports_table/sub"] = 57000, ["Module:Sports_table/totalscheck"] = 41000, ["Module:Stock_tickers/NYSE"] = 2100, ["Module:Storm_categories"] = 5200, ["Module:Storm_categories/categories"] = 5200, ["Module:Storm_categories/colors"] = 5200, ["Module:Storm_categories/icons"] = 5200, ["Module:String"] = 14200000, ["Module:String2"] = 2270000, ["Module:Su"] = 12000, ["Module:Subject_bar"] = 18000, ["Module:Suppress_categories"] = 5200, } 725fcfd17d3d7ee557e68480be6c6d224d27f65b Module:Pf 828 522 1053 1052 2023-07-10T16:51:52Z BigTa1k 2 1 revision imported wikitext text/x-wiki #REDIRECT [[Template:ParserFunction]] {{Redirect category shell| {{R from move}} }} 189e6267904ed6300f671171532db2aa0767ee3a Module:Lmd 828 523 1055 1054 2023-07-10T16:51:52Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{lx|1=Module:|2={{ucfirst:{{{1}}}}}|3=Module talk|4=talk}}<noinclude> {{documentation|Template:Ln/doc}} </noinclude> ada2a915a84c0f0ea7b317cbf6bfe01c28004751 Module:Case templates see also 828 524 1057 1056 2023-07-10T16:51:53Z BigTa1k 2 1 revision imported wikitext text/x-wiki * {{tl|R from other capitalisation}} – for categorizing [[WP:Redirect]]s from titles to article (or other pages) where the redirect is just a different capitalization * {{tl|Template capitalization}} – ?? * [[Module:String2]] [[Help:Magic words#Formatting|Magic words]] that rewrite the output (copy-paste will get the text as displayed, not as entered): * <code><nowiki>{{lc:}}</nowiki></code> – lower case output of the full text * <code><nowiki>{{uc:}}</nowiki></code> – upper case output of the full text * <code><nowiki>{{lcfirst:}}</nowiki></code> – lower case output of the first character only * <code><nowiki>{{ucfirst:}}</nowiki></code> – upper case output of the first character only <noinclude> {{Documentation|content= This list is transcluded into the documentation of the various templates it illustrates. [[Category:Related-topic templates]] }}</noinclude> 26b5326d9ef35ba8f23bf11034c0fb1c7ccb1bd4 Module:Carousel 828 525 1059 1058 2023-07-10T16:51:53Z BigTa1k 2 1 revision imported Scribunto text/plain p = {} -- carousel returns one of a list of image filenames -- -- the index of the one chosen increments every 'switchsecs' -- which is a parameter giving the number of seconds between switches -- 3600 would switch every hour -- 43200 would be every 12 hours -- 86400 would be daily (the default) -- -- The list of filenames is in a named submodule, so everyone can have their own list. -- For Komodobish (the default), the module is [[Module:Carousel/Komodobish]]. -- For Serial Number 54129, the module is [[Module:Carousel/54129]]. -- See https://en.wikipedia.org/wiki/Special:PrefixIndex/Module:Carousel/ -- -- {{#invoke:carousel | main | name = name-of-datamodule | switchsecs = number-of-seconds }} -- {{#invoke:carousel | main | name = 54129 | switchsecs = 10 }} for 10 sec switches using [[Module:Carousel/54129]] -- {{#invoke:carousel | main }} for 24 hours between switches using the default data module -- p.main = function(frame) -- get parameter switchsecs; if NaN or less than 1, set default local switchtime = tonumber(frame.args.switchsecs) or 86400 if switchtime < 1 then switchtime = 86400 end -- get parameter dataname; if missing, use default local dataname = frame.args.name or mw.text.trim(frame.args[1]) or "" if dataname == "" then dataname = "Komodobish" end -- there should be a named data module as a submodule local imgs = require("Module:Carousel/" .. dataname) local numimgs = #imgs -- 'now' increments by 1 every switchtime seconds local now = math.floor( os.time() / switchtime ) -- set an index between 1 and number of images local idx = now % numimgs + 1 return imgs[idx] end return p c6d1e3326a071126a6efd72815b2b21a3ede9a93 Module:Carousel/WPDogs 828 526 1061 1060 2023-07-10T16:51:53Z BigTa1k 2 1 revision imported Scribunto text/plain -- Module:Carousel/WPDogs -- Filename table for use in Module:Carousel -- Add filenames without the File: stuff, just the name -- Each filename goes on a new line surrounded by " -- (or by ' if there happens to be "" inside the filename -- if there's both " and ' give up on using that or rename it) -- Each line has a comma at the end. -- This one returns a caption as well - write "Filename | Caption" below: local i = { "YellowLabradorLooking new.jpg | Yellow [[Labrador Retriever]]", "Dobermann Fond Blanc.jpg | Female [[Dobermann]] with docked tail", "סאטף אנגלי.jpg | [[Staffordshire Bull Terrier]]", "Racibórz 2007 082.jpg | [[English Bulldog]], Racibórz, Poland", "Dalmatian liver stacked.jpg | [[Dalmatian dog]] stacked for show", "(2)BIR Grupp 7- KORTHÅRIG VORSTEH, J Björnkärrets Hertzogin Aida (24208119306).jpg|[[German Shorthaired Pointer]]", } return i 78c1e4260c7e8be175f81bd9d629fa90574476e1 Module:Boston Bridge 828 527 1063 1062 2023-07-10T16:51:54Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{Infobox Bridge |bridge_name = Boston Bridge |image = Boston Bridge 2014.jpg |image_size = 300px |caption =Boston Bridge in September&nbsp;2014. |carries= {{jct|state=PA|PA|48}} |crosses= [[Youghiogheny River]] |locale= [[Versailles, Pennsylvania|Versailles]] and [[Elizabeth Township, Allegheny County, Pennsylvania|Elizabeth Township]], [[Pennsylvania]] |maint= |id= |design= [[cantilever bridge]] |mainspan= |length= 1181 f |width= 30 ft |below= |traffic= |open= October&nbsp;13, 1932<ref>{{cite news |title=New Boston Bridge Opened to Traffic |url=https://www.newspapers.com/clip/29570035/the-pittsburgh-press/ |access-date=November 12, 2021 |work=[[The Pittsburgh Press]] |date=October 14, 1932 |page=4|via=Newspapers.com}} {{open access}}</ref> |closed= |toll= |map_cue= |map_image= |map_text= |map_width= |lat= |long= }} The '''Boston Bridge''' is a structure that crosses the [[Youghiogheny River]] between [[Versailles, Pennsylvania|Versailles]] and [[Elizabeth Township, Allegheny County, Pennsylvania|Elizabeth Township]], [[Pennsylvania]]. Its name is derived from the Massachusetts city only indirectly: the bridge is named for the Boston neighborhood of Elizabeth Township, which in turn was named for the New England city. The bridge, which opened on October&nbsp;13, 1932, carries [[Pennsylvania Route 48]] on two relatively narrow lanes. Many of its features were carefully preserved during a 1989 rehabilitation, down to its original pedestrian railings. Designed entirely for vehicular traffic, the structure changed the future of Elizabeth Township; the lack of streetcar tracks led to the abandonment of a line that served the then-rural community's small industrial settlements, while the newfound ease of access for motorists to the area's manufacturing regions opened up the township to [[suburbanization]]. ==References== {{Reflist}} == External links == {{commonscat}} *[http://www.pghbridges.com/mckeesport/0599-4462/boston.htm PGH Bridges] {{Crossings navbox |structure = Bridges |place = [[Youghiogheny River]] |bridge = Boston Bridge |bridge signs = [[Image:PA-48.svg|20px]] [[Pennsylvania Route 48|PA 48]] |downstream = [[P&LE Liberty Boro Bridge]] |downstream signs = [[CSX]] [[Keystone Subdivision]] |upstream = [[Sutersville Bridge]] |upstream signs = }} {{coord|40.3128|-79.8283|region:US-PA_type:landmark|display=title}} [[Category:Bridges in Allegheny County, Pennsylvania]] [[Category:Bridges completed in 1931]] [[Category:Road bridges in Pennsylvania]] [[Category:Bridges over the Youghiogheny River]] [[Category:Cantilever bridges in the United States]] [[Category:1931 establishments in Pennsylvania]] f35d6669ef86b5e078f2288373933e86dd462b5a Module:String2/doc 828 528 1065 1064 2023-07-10T16:51:55Z BigTa1k 2 1 revision imported wikitext text/x-wiki {{high-use}} {{module rating|release}} {{Lua|Module:GetParameters}} {{Lmd|String2}} The module '''String2''' contains a number of string manipulation functions that are much less commonly used than those in [[Module:String]]. Because Module:String is cascade-protected (some of its functions are used on the Main Page), it cannot be edited or maintained by template editors, only by admins. While it is true that string-handling functions rarely need maintenance, it is useful to allow that by template editors where possible, so this module may be used by template editors to develop novel functionality. The module contains three case-related calls that convert strings to first letter uppercase, sentence case or title case and two calls that are useful for working with substrings. There are other utility calls that strip leading zeros from padded numbers and transform text so that it is not interpreted as wikitext, and several other calls that solve specific problems for template developers such as finding the position of a piece of text on a given page. The functions are designed with the possibility of working with text returned from Wikidata in mind. However, a call to Wikidata may return empty, so the functions should generally fail gracefully if supplied with a missing or blank input parameter, rather than throwing an error. == Functions == === trim === The trim function simply trims whitespace characters from the start and end of the string. === title === The title function capitalises the first letter of each word in the text, apart from a number of short words recommended by [[s:U.S. Government Printing Office Style Manual/Capitalization Rules|The U.S. Government Printing Office Style Manual]]: {{xt|a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor}}. === sentence === The sentence function finds the first letter and capitalises it, then renders the rest of the text in lower case. It works properly with text containing wiki markup. Compare <code><nowiki>{{#invoke:String2|sentence|[[action game]]}}</nowiki></code> &rarr; {{#invoke:String2|sentence|[[action game]]}} with <code><nowiki>{{ucfirst:{{lc:[[action game]]}}}}</nowiki></code> &rarr; {{ucfirst:{{lc:[[action game]]}}}}. Piped wiki-links are handled as well: * <code><nowiki>{{#invoke:String2|sentence|[[trimix (breathing gas)|trimix]]}}</nowiki></code> &rarr; {{#invoke:String2|sentence|[[trimix (breathing gas)|trimix]]}} So are lists: * <code><nowiki>{{#invoke:String2 |sentence |{{hlist ||[[apples]] |[[pears]] |[[oranges]]}}}}</nowiki></code> → {{#invoke:String2 |sentence |{{hlist |[[apples]] |[[pears]] |[[oranges]]}}}} === ucfirst === The ucfirst function is similar to sentence; it renders the first alphabetical character in upper case, but leaves the capitalisation of the rest of the text unaltered. This is useful if the text contains proper nouns, but it will not regularise sentences that are ALLCAPS, for example. It also works with text containing piped wiki-links and with html lists. === findlast === * Function findlast finds the last item in a list. * The first unnamed parameter is the list. The list is trimmed of leading and trailing whitespace * The second, optional unnamed parameter is the list separator (default = comma space). The separator is ''not'' trimmed of leading and trailing whitespace (so that leading or trailing spaces can be used). * It returns the whole list if the separator is not found. One potential issue is that using Lua special pattern characters (<code>^$()%.[]*+-?</code>) as the separator will probably cause problems. {| class="wikitable" |+ Examples |- ! scope="col" | Case ! scope="col" | Wikitext ! scope="col" | Output |- | Normal usage || <code><nowiki>{{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291}}</nowiki></code> || {{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291}} |- | Space as separator || <code><nowiki>{{#invoke:String2 |findlast | 5 932 992,532 6,074,702 6,145,291 }}</nowiki></code> || {{#invoke:String2 |findlast | 5 932 992,532 6,074,702 6,145,291 }} |- | One item list || <code><nowiki>{{#invoke:String2 |findlast | 6,074,702 }}</nowiki></code> || {{#invoke:String2 |findlast | 6,074,702 }} |- | Separator not found || <code><nowiki>{{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291 |;}}</nowiki></code> || {{#invoke:String2 |findlast | 5, 932, 992,532, 6,074,702, 6,145,291 |;}} |- | List missing || <code><nowiki>{{#invoke:String2 |findlast |}}</nowiki></code> || {{#invoke:String2 |findlast |}} |} === split === The ''split'' function splits text at boundaries specified by separator and returns the chunk for the index idx (starting at 1). It can use positional parameters or named parameters (but these should not be mixed): * <code><nowiki>{{#invoke:String2 |split |text |separator |index |true/false}}</nowiki></code> * <code><nowiki>{{#invoke:String2 |split |txt=text |sep=separator |idx=index |plain=true/false}}</nowiki></code> Any double quotes (") in the separator parameter are stripped out, which allows spaces and wikitext like <code><nowiki>["[</nowiki></code> to be passed. Use <code>{{tl|!}}</code> for the pipe character <code>|</code>. If the optional plain parameter is set to <code>false / no / 0</code> then separator is treated as a Lua pattern. The default is plain=true, i.e. normal text matching. The index parameter is optional; it defaults to the first chunk of text. The [[Template:Stringsplit]] is a convenience wrapper for the split function. === stripZeros === The stripZeros functions finds the first number in a string of text and strips leading zeros, but retains a zero which is followed by a decimal point. For example: "0940" &rarr; "940"; "Year: 0023" &rarr; "Year: 23"; "00.12" &rarr; "0.12" === nowiki === The nowiki function ensures that a string of text is treated by the MediaWiki software as just a string, not code. It trims leading and trailing whitespace. === val2percent === The val2percent functions scans through a string, passed as either the first unnamed parameter or |txt=, and converts each number it finds into a percentage, then returns the resulting string. === one2a === The one2a function scans through a string, passed as either the first unnamed parameter or |txt=, and converts each occurrence of 'one ' into either 'a ' or 'an ', then returns the resultant string. The [[Template:One2a]] is a convenience wrapper for the one2a function. === findpagetext === The findpagetext function returns the position of a piece of text in the wikitext source of a page. It takes up to four parameters: * First positional parameter or |text is the text to be searched for. * Optional parameter |title is the page title, defaults to the current page. * Optional parameter |plain is either true for a plain search (default), or false for a [[Extension:Scribunto/Lua reference manual #Patterns|Lua pattern]] search. * Optional parameter |nomatch is the value returned when no match is found; default is nothing. ; Examples : <code><nowiki>{{#invoke:String2 |findpagetext |text=Youghiogheny}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=Youghiogheny}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=Youghiogheny |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=Youghiogheny |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=Youghiogheny |title=Boston Bridge |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=Youghiogheny |title=Boston Bridge |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=river |title=Boston Bridge |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=river |title=Boston Bridge |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=[Rr]iver |title=Boston Bridge |plain=false |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=[Rr]iver |title=Boston Bridge |plain=false |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=%[%[ |title=Boston Bridge |plain=f |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=%[%[ |title=Boston Bridge |plain=f |nomatch=not found}} : <code><nowiki>{{#invoke:String2 |findpagetext |text=%{%{[Cc]oord |title=Boston Bridge |plain=f |nomatch=not found}}</nowiki></code> → {{#invoke:String2 |findpagetext |text=%{%{coord |title=Boston Bridge |plain=f |nomatch=not found}} The search is case-sensitive, so Lua pattern matching is needed to find <code>river</code> or <code>River</code>. The last example finds <code><nowiki>{{coord</nowiki></code> and <code><nowiki>{{Coord</nowiki></code>. The penultimate example finds a wiki-link. The [[Template:Findpagetext]] is a convenience wrapper for this function. === strip === The strip function strips the first positional parameter of the characters or pattern supplied in the second positional parameter. === matchAny === {{for|a function to replace multiple patterns|Module:MultiReplace}} The matchAny function returns the index of the first positional parameter to match the ''source'' parameter. If the ''plain'' parameter is set to false (default true) then the search strings are Lua patterns. This can usefully be put in a switch statement to pick a switch case based on which pattern a string matches. Returns the empty string if nothing matches, for use in {{pf|if}}. <code>{{((}}#invoke:String2|matchAny|123|abc|source=abc 124}}</code> returns 2. === hyphen2dash === Extracted hyphen_to_dash() function from [[Special:Permalink/1017669505|Module:Citation/CS1]]. Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are returned unmodified. These forms are modified: * letter - letter (A - B) * digit - digit (4-5) * digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) * letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) * digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d) Any other forms are returned unmodified. The input string may be a comma- or semicolon-separated list. Semicolons are converted to commas. <code><nowiki>{{</nowiki>#invoke:String2|hyphen2dash|1=1-2<nowiki>}}</nowiki></code> returns {{#invoke:String2|hyphen2dash|1=1-2}}. <code><nowiki>{{</nowiki>#invoke:String2|hyphen2dash|1=1-2; 4–10<nowiki>}}</nowiki></code> returns {{#invoke:String2|hyphen2dash|1=1-2; 4–10}}. [[Help:Citation Style 1#Accept-this-as-written markup|Accept-this-as-written markup]] is supported, e.g. <code><nowiki>{{</nowiki>#invoke:String2|hyphen2dash|1=((1-2)); 4–10<nowiki>}}</nowiki></code> returns {{#invoke:String2|hyphen2dash|1=((1-2)); 4–10}}. By default, a normal space is inserted after the separating comma in lists. An optional second parameter allows to change this to a different character (i.e. a thin space or hair space). ===startswith=== A startswith function similar to {{ml|string|endswith}}. Both parameters are required, although they can be blank. Leading and trailing whitespace ''is'' counted, use named parameters to avoid this if required. Outputs "yes" for true and blank for false so may be passed directly to #if. {{markup| <nowiki>{{#invoke:string2|startswith|search|se}}</nowiki>|{{#invoke:string2|startswith|search|se}}| <nowiki>{{#invoke:string2|startswith|search|ch}}</nowiki>|{{#invoke:string2|startswith|search|ch}}}}<!-- Template:Mra --> == Usage == * <code><nowiki>{{#invoke:String2 | sentence |…}}</nowiki></code> - Capitalizes the first character and shifts the rest to lowercase ** Although similar to [[Help:Magic_words#Formatting|magic words]]' <code><nowiki>{{ucfirst:}}</nowiki></code> function, this call works even with piped wiki-links because it searches beyond leading brackets and other non-alphanumeric characters. ** It now also recognises when it has an html list passed to it and capitalises the first alphabetic letter beyond the list item markup ({{tag|li|o}}) and any piped links that may be there. * <code><nowiki>{{#invoke:String2 | ucfirst |…}}</nowiki></code> - Capitalizes the first alphabetic character and leaves the rest unaltered ** Works with piped wiki-links and html lists * <code><nowiki>{{#invoke:String2 | title |…}}</nowiki></code> - Capitalizes all words, except for <code>a</code>, <code>an</code>, <code>the</code>, <code>at</code>, <code>by</code>, <code>for</code>, <code>in</code>, <code>of</code>, <code>on</code>, <code>to</code>, <code>up</code>, <code>and</code>, <code>as</code>, <code>but</code>, <code>or</code>, and <code>nor</code>. * <code><nowiki>{{#invoke:String2 | stripZeros |…}}</nowiki></code> - Removes leading padding zeros from the first number it finds in the string * <code><nowiki>{{#invoke:String2 | title |…}}</nowiki></code> - Renders the string as plain text without wikicode === Parameters === These functions take one unnamed parameter comprising (or invoking as a string) the text to be manipulated: * title * sentence * ucfirst == Examples == {| class="wikitable" ! scope="col" | Input ! scope="col" | Output |- | <nowiki>{{#invoke:String2| ucfirst | abcd }}</nowiki> | {{#invoke:String2| ucfirst | abcd }} |- | <nowiki>{{#invoke:String2| ucfirst | abCD }}</nowiki> | {{#invoke:String2| ucfirst | abCD }} |- | <nowiki>{{#invoke:String2| ucfirst | ABcd }}</nowiki> | {{#invoke:String2| ucfirst | ABcd }} |- | <nowiki>{{#invoke:String2| ucfirst | ABCD }}</nowiki> | {{#invoke:String2| ucfirst | ABCD }} |- | <nowiki>{{#invoke:String2| ucfirst | 123abcd }}</nowiki> | {{#invoke:String2| ucfirst | 123abcd }} |- | <nowiki>{{#invoke:String2| ucfirst | }}</nowiki> | {{#invoke:String2| ucfirst | }} |- | <nowiki>{{#invoke:String2| ucfirst | human X chromosome }}</nowiki> | {{#invoke:String2| ucfirst | human X chromosome}} |- | <nowiki>{{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue</nowiki><br /><nowiki>| P136 |fetchwikidata=ALL |onlysourced=no |qid=Q1396889}} }}</nowiki> | {{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue |P136 |fetchwikidata=ALL |onlysourced=no |qid=Q1396889}} }} |- | <nowiki>{{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue</nowiki><br /><nowiki>| P106 |fetchwikidata=ALL |list=hlist |qid=Q453196}} }}</nowiki> | {{#invoke:String2 | ucfirst | {{#invoke:WikidataIB |getValue |P106 |fetchwikidata=ALL |list=hlist |qid=Q453196}} }} |- | &nbsp; | |- | <nowiki>{{#invoke:String2| sentence | abcd }}</nowiki> | {{#invoke:String2| sentence | abcd }} |- | <nowiki>{{#invoke:String2| sentence | abCD }}</nowiki> | {{#invoke:String2| sentence | abCD }} |- | <nowiki>{{#invoke:String2| sentence | ABcd }}</nowiki> | {{#invoke:String2| sentence | ABcd }} |- | <nowiki>{{#invoke:String2| sentence | ABCD }}</nowiki> | {{#invoke:String2| sentence | ABCD }} |- | <nowiki>{{#invoke:String2| sentence | [[action game]] }}</nowiki> | {{#invoke:String2| sentence | [[action game]] }} |- | <nowiki>{{#invoke:String2| sentence | [[trimix (breathing gas)|trimix]] }}</nowiki> | {{#invoke:String2| sentence | [[trimix (breathing gas)|trimix]] }} |- | <nowiki>{{#invoke:String2| sentence | }}</nowiki> | {{#invoke:String2| sentence | }} |- | &nbsp; | |- | <nowiki>{{#invoke:String2| title | abcd }}</nowiki> | {{#invoke:String2| title | abcd }} |- | <nowiki>{{#invoke:String2| title | abCD }}</nowiki> | {{#invoke:String2| title | abCD }} |- | <nowiki>{{#invoke:String2| title | ABcd }}</nowiki> | {{#invoke:String2| title | ABcd }} |- | <nowiki>{{#invoke:String2| title | ABCD }}</nowiki> | {{#invoke:String2| title | ABCD }} |- | <nowiki>{{#invoke:String2| title | }}</nowiki> | {{#invoke:String2| title | }} |- | <nowiki>{{#invoke:String2| title | the vitamins are in my fresh california raisins}}</nowiki> | {{#invoke:String2| title | the vitamins are in my fresh california raisins}} |- |} === String split === [[Template:String split]] is a convenience wrapper for the split function. * <code><nowiki>{{String split |This is a piece of text to be split |" "}}</nowiki></code> → {{String split |This is a piece of text to be split |" "}} * <code><nowiki>{{String split |This is a piece of text to be split |" "| 4}}</nowiki></code> → {{String split |This is a piece of text to be split |" "| 4}} * <code><nowiki>{{String split |This is a piece of text to be split |x| 2}}</nowiki></code> → {{String split |This is a piece of text to be split |x| 2}} Modules may return strings with | as separators like this: <code><nowiki>{{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }}</nowiki></code> → {{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }} * <code><nowiki>{{String split |{{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }}|{{!}}| 2}}</nowiki></code> → {{String split |{{#invoke:carousel | main | name = WPDogs | switchsecs = 5 }}|{{!}}| 2}} Lua patterns can allow splitting at classes of characters such as punctuation: * <code><nowiki>{{String split |Apples, pears, oranges; Cats, dogs|"%p"| 2 |false}}</nowiki></code> → {{String split |Apples, pears, oranges; Cats, dogs|"%p"| 2 |false}} * <code><nowiki>{{String split |Apples, pears, oranges; Cats, dogs|"%p"| 4 |false}}</nowiki></code> → {{String split |Apples, pears, oranges; Cats, dogs|"%p"| 4 |false}} Or split on anything that isn't a letter (no is treated as false): * <code><nowiki>{{String split |Apples pears oranges; Cats dogs|"%A+"| 4 |no}}</nowiki></code> → {{String split |Apples pears oranges; Cats dogs|"%A+"| 4 |no}} Named parameters force the trimming of leading and trailing spaces in the parameters and are generally clearer when used: * <code><nowiki>{{String split | txt=Apples pears oranges; Cats dogs | sep="%A+" | idx=3 | plain=false }}</nowiki></code> → {{String split | txt=Apples pears oranges; Cats dogs | sep="%A+" | idx=3 | plain=false }} === One2a === [[Template:One2a]] is a convenience wrapper for the one2a function. Capitalisation is kept. Aimed for usage with {{tl|Convert}}. * <code><nowiki>{{one2a |One foot. One mile. One kilometer. One inch.One amp. one foot. one mile. one inch. Alone at last. Onely the lonely. ONE ounce. One monkey.}}</nowiki></code> → :{{one2a |One foot. One mile. One kilometer. One inch.One amp. one foot. one mile. one inch. Alone at last. Onely the lonely. ONE ounce. One monkey.}} * <code><nowiki>{{convert|1|ft|spell=on}}</nowiki></code> → {{convert|1|ft|spell=on}} * <code><nowiki>{{one2a|{{convert|1|ft|spell=on}}}}</nowiki></code> → {{one2a|{{convert|1|ft|spell=on}}}} * <code><nowiki>{{convert|2.54|cm|0|disp=out|spell=on}}</nowiki></code> → {{convert|2.54|cm|0|disp=out|spell=on}} * <code><nowiki>{{one2a|{{convert|2.54|cm|0|disp=out|spell=on}}}}</nowiki></code> → {{one2a|{{convert|2.54|cm|0|disp=out|spell=on}}}} == See also == [[Module:String]] for the following functions: * len * sub * sublength * match * pos * str_find * find * replace * rep Templates and modules related to capitalization {{Case templates see also}} Templates that implement {{tag|nowiki|o}} * {{tl|nowiki}} * {{tl|nowiki2}} <includeonly>{{Sandbox other|| [[Category:Modules that manipulate strings|*]] }}</includeonly> 16cb4393067392cf526ac4ea120a04fca4da7ea0 File:CoverartMech1.png 6 529 1068 2023-07-10T17:33:54Z BigTa1k 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:CoverartMech2.png 6 530 1069 2023-07-10T17:34:28Z BigTa1k 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 MechAssault 0 494 1070 989 2023-07-10T18:57:00Z BigTa1k 2 wikitext text/x-wiki ''MechAssault'' is a third-person shooter/mecha game released in 2002, developed by Day 1 Studios and published by Microsoft for the original Xbox. Centered in the BattleTech universe, the game follows a group of people under the employment of a mercenary company called the Wolf's Dragoons, and the operations they carry out on the planet Helios. MechAssault is notable for contributing to the rise of online gaming on game consoles, given that the Xbox had native support for broadband/Ethernet connections, and though the services for original Xbox Live have been shut down for a while, has managed to survive through alternatives such as Xlink Kai and Insignia. == Plot == (describe characters, setting, and a summary of all missions) == Multiplayer == (mention original xbox live, xlink/insig, vanilla meta, etc) == Modding == (Rebalance, Multi expanded, grinder enhanced) == Related == (page for Mech2) (phantom war) (mech4 mercs) (wolves) (separate pages for modding, online, emulators) 3c44f860840db6655fc823735e03afc689936927 1071 1070 2023-07-10T19:01:58Z BigTa1k 2 wikitext text/x-wiki [[File:CoverartMech1.png|thumb|Cover Art for Mech1]] ''MechAssault'' is a third-person shooter/mecha game released in 2002, developed by Day 1 Studios and published by Microsoft for the original Xbox. Centered in the BattleTech universe, the game follows a group of people under the employment of a mercenary company called the Wolf's Dragoons, and the operations they carry out on the planet Helios. MechAssault is notable for contributing to the rise of online gaming on game consoles, given that the Xbox had native support for broadband/Ethernet connections, and though the services for original Xbox Live have been shut down for a while, has managed to survive through alternatives such as Xlink Kai and Insignia. == Plot == (describe characters, setting, and a summary of all missions) == Multiplayer == (mention original xbox live, xlink/insig, vanilla meta, etc) == Modding == (Rebalance, Multi expanded, grinder enhanced) == Related == (page for Mech2) (phantom war) (mech4 mercs) (wolves) (separate pages for modding, online, emulators) bd6bb1a8a6bb8fb93b8c011bf15631ccd9a615c9 1077 1071 2023-07-11T14:48:14Z BigTa1k 2 skeleton page, will fill out later wikitext text/x-wiki [[File:CoverartMech1.png|thumb|Cover Art for Mech1, featuring a Cougar firing a PPC]] ''MechAssault'' is a third-person shooter/mecha game released in 2002, developed by Day 1 Studios and published by Microsoft for the original Xbox. Centered in the BattleTech universe, the game follows a group of people under the employment of a mercenary company called the Wolf's Dragoons, and the operations they carry out on the planet Helios. MechAssault is notable for contributing to the rise of online gaming on game consoles, given that the Xbox had native support for broadband/Ethernet connections, and though the services for original Xbox Live have been shut down for a while, has managed to survive through alternatives such as Xlink Kai and Insignia. == Plot == (describe characters, setting, and a summary of all missions) == Multiplayer == ''Main Article: [[MechAssault/Multiplayer|Multiplayer]]'' (mention original xbox live, xlink/insig, vanilla meta, etc) For a guide on playing online today, see [[Getting online in 2023|this page]]. == Modding == ''Main Article: [[MechAssault/Mods|Mods]]'' (Rebalance, Multi expanded, grinder enhanced) == Related == (page for Mech2) (phantom war) (mech4 mercs) (wolves) (separate pages for modding, online, emulators) bfe91b310266cffc834302c37c564c8003a5b773 File:Fav.ico 6 531 1072 2023-07-11T13:30:06Z BigTa1k 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 MechAssault/Multiplayer 0 532 1073 2023-07-11T13:46:02Z BigTa1k 2 Created page with "Test description " wikitext text/x-wiki Test description 11f2134bf570ca5f2dc7d1a44be25a2ce8b48259 1075 1073 2023-07-11T14:30:31Z BigTa1k 2 skeleton page, will fill out later wikitext text/x-wiki Test description == Guide == == Grinder == == Destruction-based modes == Test description === Metagame === == Objective modes == == Mods == == Notes == 40b865c46e766151d3d28ea78c94c67676422f19 Getting online in 2023 0 533 1074 2023-07-11T14:26:37Z BigTa1k 2 skeleton page, will fill out later wikitext text/x-wiki xbox live dead, xlink insig == Prerequisites == * an original Xbox or a PC (for emulation) * an Ethernet cable with access to a router (strongly advised) * knowledge regarding FTPing files from a PC to an Xbox (if going down that route == Which one is right for me? == === Xlink vs. Insignia === === CXBX vs. Xemu === == Procedure: Original Xbox (Xlink) == ''Wired:'' # ''Wireless:'' # == Procedure: Emulator (Xlink) == === CXBX === ''Wired:'' # ''Wifi:'' # === Xemu === ''Wired:'' # ''Wifi:'' # == Procedure: Original Xbox (Insignia) == == Procedure: Emulator (Insignia) == === CXBX === Insignia is not known to work on CXBX at this current time === Xemu === Blah blah == Troubleshooting == === Xlink connection errors === === Insignia connection errors === === Emulator connection errors === === Missing DLC === === Mod conflicts === == Notes == * Though it is possible to run a setup with as little as no Ethernet cables, it is strongly advised against doing so due to conflicting NAT types, poor connection, etc. * Insignia servers have an unofficial ban on mods running on their servers * Filezilla is known to have problems with FTPing large amounts of files to an Xbox, for this reason WinSCP is recommended over it * It will be hard to find games if one is not a part of the already heavily splintered community, though one can ask around on Xlink if vanilla/Rebalance games are being played, and games tend to become more common during the later hours of the weekend; Insignia has their own Discord server but since mods are generally not accepted there it is advised from using this method * Vanilla Mech1 games tend to be a rarity given that the Rebalance mod has largely superseded it 7add166725d4b0aba450f6de389b973932d09d3b MechAssault/Mods 0 534 1076 2023-07-11T14:43:32Z BigTa1k 2 skeleton page, will fill out later wikitext text/x-wiki Overview == Single player mods == == Multiplayer mods == === Rebalance === ''Main Article: MechAssault Rebalance'' == Notes == a1d1b171beab014b67f84efaebd647381baa0284 Main Page 0 1 1078 4 2023-08-12T22:03:31Z YeetusDeleteus 6 added a slight change to the main page wikitext text/x-wiki __NOTOC__ == Welcome to the {{SITENAME}}! == This wiki page contains information about MechAssault and Rebalance as a whole ca693132a52fef0889f81951e053a3212ac5a130